$engine JScript
$uname ExtendedSearch
$dname Расширенный поиск
$addin global
$addin stdcommands
$addin stdlib
$addin hotkeys
////////////////////////////////////////////////////////////////////////////////////////
////{ Cкрипт "Расширенный поиск" (extSearch.js) для проекта "Снегопат"
////
//// Описание: Реализует поиск текста при помощи регулярных выражений в активном окне редактора.
//// Автор: Александр Кунташов <kuntashov@gmail.com>, http://compaud.ru/blog
////}
////////////////////////////////////////////////////////////////////////////////////////
stdlib.require('TextWindow.js', SelfScript);
stdlib.require('ScriptForm.js', SelfScript);
global.connectGlobals(SelfScript);
////////////////////////////////////////////////////////////////////////////////////////
////{ Макросы
////
SelfScript.self['macrosНайти текст'] = function() {
var w = GetTextWindow();
if (!w) return false;
var es = GetExtSearch();
var selText = w.GetSelectedText();
if (selText == '')
selText = w.GetWordUnderCursor();
es.setSimpleQuery(selText);
es.show();
if (selText == '')
{
es.clearSearchResults();
es.setDefaultSearchQuery();
}
else
es.searchActiveDoc(true);
return true;
}
SelfScript.self['macrosНайти во всех открытых документах'] = function() {
var w = GetTextWindow();
if (!w) return false;
var es = GetExtSearch();
var selText = w.GetSelectedText();
if (selText == '')
selText = w.GetWordUnderCursor();
es.setSimpleQuery(selText);
es.show();
if (selText == '')
{
es.clearSearchResults();
es.setDefaultSearchQuery();
}
else
es.searchOpenedWindows(true);
return true;
}
SelfScript.self['macrosГлобальный поиск'] = function() {
var es = GetExtSearchGlobal();
var w = GetTextWindow();
if (!w) {
var selText = '';
} else {
var selText = w.GetSelectedText();
if (selText == '')
selText = w.GetWordUnderCursor();
}
es.isGlobalFind = true;
es.activeView = windows.getActiveView();
es.isInCurrentMdConteinerFind = false;
es.setSimpleQuery(selText);
es.show();
if (selText == '')
{
es.clearSearchResults();
es.setDefaultSearchQuery();
}
else
es.searchInMetadata(true);
return true;
}
SelfScript.self['macrosГлобальный поиск по текущему контейнеру'] = function() {
//Текущий контейнер метаданных определяем по активному окну.
//будет открыта внешняя обработка, занчит ищем глобально только по этой обработке.
//открыт cf файл или же cf базы данных и мы находимся в текстовом модуле определенной
//конфигурации, значит искать будет по текущей контейнеру.
var es = GetExtSearchGlobal();
var w = GetTextWindow();
if (!w) {
var selText = '';
} else {
var selText = w.GetSelectedText();
if (selText == '')
selText = w.GetWordUnderCursor();
}
es.isGlobalFind = true;
es.activeView = windows.getActiveView();
es.isInCurrentMdConteinerFind = true;
es.setSimpleQuery(selText);
es.show();
if (selText == '')
{
es.clearSearchResults();
es.setDefaultSearchQuery();
}
else
es.searchInMetadata(true);
return true;
}
SelfScript.self['macrosГл поиск фильтр по метаданным'] = function() {
var es = GetExtSearchGlobal();
if (es.isGlobalFind){
md = stdlib.require(stdlib.getSnegopatMainFolder() + 'scripts\\mdNavigator.js');
if (es.filterByUUID){
es.vtMD = {};
es.filterByUUID = null;
}
es.filterByUUID = md.SelectMdUUID();
}
es.show();
return true;
}
SelfScript.self['macrosОтменить глобальный поиск'] = function() {
var es = GetExtSearchGlobal();
if (es.startGlobalSearch){
es.startGlobalSearch = false;
}
}
//// МАКРОСЫ С ПРЕДВАРИТЕЛЬНЫМ ОТКРЫТИЕМ ДИАЛОГА ДЛЯ НАСТРОЙКИ ПАРАМЕТРОВ ПОИСКА
SelfScript.self['macrosНайти текст в текущем модуле (с диалогом)'] = function() {
openSearchDialog(SearchAreas.ActiveWindow);
return true;
}
SelfScript.self['macrosНайти текст в открытых окнах (с диалогом)'] = function() {
openSearchDialog(SearchAreas.AllOpenedWindows);
return true;
}
SelfScript.self['macrosГлобальный поиск (с диалогом)'] = function() {
openSearchDialog(SearchAreas.Global);
return true;
}
//// МАКРОСЫ ДЛЯ УПРАВЛЕНИЯ ОКНОМ РЕЗУЛЬТАТОВ ПОИСКА
SelfScript.self['macrosОткрыть окно поиска'] = function() {
GetExtSearch().show();
}
SelfScript.self['macrosОткрыть окно глобального поиска'] = function() {
GetExtSearchGlobal().show();
}
SelfScript.self['macrosЗакрыть окно поиска'] = function() {
var es = GetExtSearch();
if (es.isOpen()) {
es.close();
return true;
}
es = GetExtSearchGlobal();
if (es.isOpen()) {
es.close();
return true;
}
return false;
}
SelfScript.self['macrosПерейти к следующему совпадению'] = function() {
var es = GetExtSearch();
es.show();
es.moveRowCursor(true);
}
SelfScript.self['macrosПерейти к предыдущему совпадению'] = function() {
var es = GetExtSearch();
es.show();
es.moveRowCursor(false);
}
SelfScript.self['macrosСвернуть группировки'] = function() {
var es = GetExtSearch();
es.expandTree(true);
}
SelfScript.self['macrosРазвернуть группировки'] = function() {
var es = GetExtSearch();
es.expandTree(false);
}
/* Возвращает название макроса по умолчанию - вызывается, когда пользователь
дважды щелкает мышью по названию скрипта в окне Снегопата. */
function getDefaultMacros() {
return 'Открыть окно поиска';
}
////} Макросы
////////////////////////////////////////////////////////////////////////////////////////
////{ ExtSearch - Расширенный поиск в тексте модуля.
////
RowTypes = {
'SearchResult' : 0, // Строка результата поиска.
'ProcGroup' : 1, // Строка группы-процедуры (в режиме группировки по процедурам и функциям).
'FuncGroup' : 2, // Строка группы-функции (в режиме группировки по процедурам и функциям).
'SearchDoc' : 3 // Строка документа, в котором производится поиск.
}
RE = {
METHOD_START : /^\s*((?:procedure)|(?:function)|(?:процедура)|(?:функция))\s+([\wА-яёЁ\d]+)\s*\(/i,
METHOD_END : /((?:EndProcedure)|(?:EndFunction)|(?:КонецПроцедуры)|(?:КонецФункции))/i
}
SearchAreas = {
'ActiveWindow' : 0, // В текущем модуле
'AllOpenedWindows' : 1, // Во всех открытых окнах
'Global' : 2, // Глобально (во всех модулях основной конфигурации)
'CurrentContainer' : 3 // В текущем открытом контейнере (внешней обработке, конфигурации ИБ и т.п.)
};
/* Осуществляет поиск с предварительным открытием диалогового окна. */
function openSearchDialog(initSearchArea) {
if (!initSearchArea)
initSearchArea = SearchAreas.ActiveWindow;
var w = GetTextWindow();
if (!w) return false;
var selText = w.GetSelectedText();
if (selText == '')
selText = w.GetWordUnderCursor();
var sDlg = new ExtSearchDialog(selText, initSearchArea);
if (sDlg.show(true) == true)
{
var searchQuery = sDlg.getSearchQueryParams();
if (searchQuery.Query == '')
{
var es = GetExtSearch();
es.clearSearchResults();
es.setDefaultSearchQuery();
}
else
{
switch(sDlg.getSearchArea())
{
case SearchAreas.AllOpenedWindows:
var es = GetExtSearch();
es.setQuery(searchQuery);
es.show();
es.searchOpenedWindows(true);
break;
case SearchAreas.CurrentContainer:
var es = GetExtSearchGlobal();
es.isGlobalFind = true;
es.activeView = windows.getActiveView();
es.isInCurrentMdConteinerFind = true;
es.setQuery(searchQuery);
es.show();
es.searchInMetadata(true);
break;
case SearchAreas.Global:
var es = GetExtSearchGlobal();
es.isGlobalFind = true;
es.activeView = windows.getActiveView();
es.isInCurrentMdConteinerFind = false;
es.setQuery(searchQuery);
es.show();
es.searchInMetadata(true);
break;
case SearchAreas.ActiveWindow:
default:
var es = GetExtSearch();
es.setQuery(searchQuery);
es.show();
es.searchActiveDoc(true);
break;
}
}
}
}
/* Реализует диалог настройки параметров поиска.*/
ExtSearchDialog = ScriptForm.extend({
settingsRootPath : SelfScript.uniqueName,
settings : {
pflSnegopat : {
'IsRegExp' : false, // Поиск регулярными выражениями.
'CaseSensetive' : false, // Учитывать регистр при поиске.
'WholeWords' : false, // Поиск слова целиком.
'SearchHistory' : v8New('ValueList'), // История поиска.
'HistoryDepth' : 15, // Количество элементов истории поиска.
'TreeView' : false // Группировать результаты поиска по методам.
}
},
construct : function (query, initSearchArea) {
this._super("scripts\\extSearch.ssf");
this.form.КлючСохраненияПоложенияОкна = "extSearch.dialog.js"
this.loadSettings();
this.form.Query = query;
this.form.SearchArea = initSearchArea;
},
getSearchQueryParams: function () {
var params = v8New('Structure');
params.Insert('Query', this.form.Query);
params.Insert('WholeWords', this.form.WholeWords);
params.Insert('CaseSensetive', this.form.CaseSensetive);
params.Insert('IsRegExp', this.form.IsRegExp);
return params;
},
getSearchArea: function () {
return this.form.SearchArea;
},
Form_OnClose : function () {
this.saveSettings();
},
Query_StartListChoice : function (control, defaultHandler) {
control.val.ChoiceList = this.form.SearchHistory;
},
btFind_Click: function (btn) {
this.close(true);
},
btCancel_Click: function (btn) {
this.close(false);
},
IsRegExp_OnChange : function(Элемент) {
if (this.form.IsRegExp)
this.form.WholeWords = false;
},
WholeWords_OnChange : function(Элемент) {
if (this.form.WholeWords)
this.form.IsRegExp = false;
}
}); // end of ExtSearchDialog
ExtSearch = ScriptForm.extend({
settingsRootPath : SelfScript.uniqueName,
settings : {
pflSnegopat : {
'IsRegExp' : false, // Поиск регулярными выражениями.
'CaseSensetive' : false, // Учитывать регистр при поиске.
'WholeWords' : false, // Поиск слова целиком.
'SearchHistory' : v8New('ValueList'), // История поиска.
'HistoryDepth' : 15, // Количество элементов истории поиска.
'TreeView' : false // Группировать результаты поиска по методам.
}
},
construct : function (isExtend) {
if (isExtend == undefined) isExtend = false;
this._super("scripts\\extSearch.results.ssf");
this.form.КлючСохраненияПоложенияОкна = "extSearch.js"
this.results = this.form.Controls.SearchResults.Value;
this.results.Columns.Add('_method');
this.results.Columns.Add('groupsCache');
this.results.Columns.Add('_object');
this.results.Columns.Add('_match');
this.results.Columns.Add('SortMetadata');
this.watcher = new TextWindowsWatcher();
this.watcher.startWatch();
this.loadSettings();
this.targetWindow = null;
this.Icons = {
'Func': this.form.Controls.PicFunc.Picture,
'Proc': this.form.Controls.PicProc.Picture
}
this.SearchDocRowFont = v8New('Font', undefined, undefined, true);
this.isGlobalFind = false;
this.SetControlsVisible();
if (!isExtend) ExtSearch._instance = this;
},
setSimpleQuery : function (query) {
this.form.Query = query;
this.form.IsRegExp = false;
this.form.CaseSensetive = false;
this.addToHistory(query);
},
setQuery : function (searchQueryParams) {
this.form.Query = searchQueryParams.Query;
this.form.IsRegExp = searchQueryParams.IsRegExp;
this.form.CaseSensetive = searchQueryParams.CaseSensetive;
this.form.WholeWords = searchQueryParams.WholeWords;
this.addToHistory(this.form.Query);
},
expandTree : function (collapse) {
var tree = this.form.Controls.SearchResults;
for (var i=0; i < this.results.Rows.Count(); i++)
{
var docRow = this.results.Rows.Get(i);
if (this.form.TreeView)
{
for (var j=0; j < docRow.Rows.Count(); j++)
{
var row = docRow.Rows.Get(j);
collapse ? tree.Collapse(row) : tree.Expand(row, true);
}
}
else
{
collapse ? tree.Collapse(docRow) : tree.Expand(docRow, true);
}
}
},
getWindowObject : function (view) {
if (view.mdObj && view.mdProp)
return new MdObject(view.mdObj, view.mdProp, view.title);
var obj = view.getObject();
if (obj && toV8Value(obj).typeName(0) == 'TextDocument')
return new TextDocObject(obj, view.title);
if (obj) Message('Неподдерживаемый тип объекта для поиска: ' + toV8Value(obj).typeName(0));
return null;
},
searchOpenedWindows: function (fromHotKey) {
var activeWindow = this.watcher.getActiveTextWindow();
if (!activeWindow) return;
var activeView = activeWindow.GetView();
if (!activeView) return;
this.clearSearchResults();
this.re = this.buildSearchRegExpObject();
if (!this.re) return;
var activeWndResRow = null;
var es = this;
(function (views) {
for(var i = 0; i < views.count; i++)
{
var v = views.item(i);
if(v.isContainer != vctNo)
{
// Если окно - контейнер, то обходим рекурсивно его потомков.
arguments.callee(v.enumChilds());
continue;
}
var obj = es.getWindowObject(v);
if (!obj) continue;
var docRow = es.search(obj, es.re);
if (v == activeView)
activeWndResRow = docRow;
}
})(windows.mdiView.enumChilds());
this.showSearchResult(activeWndResRow, fromHotKey);
},
searchActiveDoc : function (fromHotKey) {
this.clearSearchResults();
var activeWindow = this.watcher.getActiveTextWindow();
if (!activeWindow) return;
this.re = this.buildSearchRegExpObject();
if (!this.re) return;
var obj = this.getWindowObject(activeWindow.GetView());
if (!obj) return;
var docRow = this.search(obj, this.re);
this.showSearchResult(docRow, fromHotKey);
},
buildSearchRegExpObject : function () {
var pattern = this.form.Query;
var reFlags = '';
if (!this.form.IsRegExp)
{
pattern = StringUtils.addSlashes(pattern);
if (this.form.WholeWords)
pattern = "([^\\w\\dА-я]|^)" + pattern + "([^\\w\\dА-я]|$)";
}
else
{
if(pattern.replace("\\\\", "").search(/\\r|\\n/) != -1)
reFlags = 'gm';
}
if(!this.form.CaseSensetive)
reFlags += 'i';
var re = null;
try
{
re = new RegExp(pattern, reFlags);
}
catch (e)
{
DoMessageBox("В регулярном выражении допущена ошибка: \n" + e.message);
return null;
}
return re;
},
search : function (obj, re) {
var docRow = this.results.Rows.Add();
docRow.FoundLine = obj.getTitle();
docRow._object = obj;
docRow.RowType = RowTypes.SearchDoc;
if (!obj.sort) obj.sort = 999;
var strSort = "0000000000"+(obj.sort + this.results.Rows.Count());
strSort = strSort.substr(strSort.length-10);
docRow.SortMetadata = strSort;
docRow.groupsCache = v8New('Map');
if(!re.multiline)
{
var curMethod = {
'Name' : 'Раздел описания переменных',
'IsProc' : undefined,
'StartLine' : 0
}
var lines = StringUtils.toLines(obj.getText());
for(var lineIx=0; lineIx < lines.length; lineIx++)
{
var line = lines[lineIx];
// Проверим, не встретилось ли начало метода.
var matches = line.match(RE.METHOD_START);
if (matches && matches.length)
{
curMethod = {
'Name' : matches[2],
'IsProc' : matches[1].toLowerCase() == 'процедура' || matches[1].toLowerCase() == 'procedure',
'StartLine' : lineIx
}
}
matches = line.match(re);
if (matches && matches.length)
this.addSearchResult(docRow, line, lineIx + 1, matches, curMethod);
// Проверим, не встретился ли конец метода.
matches = line.match(RE.METHOD_END);
if (matches && matches.length)
{
curMethod = {
'Name' : '<Текст вне процедур и функций>',
'IsProc' : undefined,
'StartLine' : lineIx
}
}
}
}
else
{
//debugger
// Это многострочный поиск
// Для начала надо вообще проверить, находится ли что-нибудь
var text = obj.getText()
var results = [], r
while(r = re.exec(text))
results.push(r)
if(results.length) // Что-то нашли. Теперь надо получить номера строк для каждого вхождения
{
this.form.TreeView = false;
var idx = 0, lineNum = 0, currentRes = results[idx], beginIdx = currentRes.index
// Для исключение ситуации, когда текст найден в последней строке, не заканчивающейся переводом строки,
// добавим к тексту перевод строки
text += '\n';
re = /.*\n/g
while(r = re.exec(text))
{
lineNum++
if(r.index <= beginIdx && r.lastIndex > beginIdx)
{
currentRes.index -= r.index
currentRes.lastIndex -= r.index
// Для отображения результата многострочного поиска преобразуем строку
currentRes.realResult = currentRes[0]
currentRes[0] = currentRes[0].replace(/^\s+/, '').replace(/\n\s*/g, ' \u00BB ').substr(0, 50) + '\n'
this.addSearchResult(docRow, r[0], lineNum, results[idx]);
idx++;
if(idx == results.length)
break;
currentRes = results[idx]
beginIdx = currentRes.index
}
}
}
}
if (this.form.TreeView && docRow.Rows.Count() > 0)
{
var lastGroup = this.results.Rows.Get(this.results.Rows.Count() - 1);
if (lastGroup.FoundLine == '<Текст вне процедур и функций>')
lastGroup.FoundLine = "Раздел основной программы";
}
if (!docRow.Rows.Count())
{
this.results.Rows.Delete(docRow);
docRow = null;
}
return docRow;
},
showResult: function(docRow, fromHotKey){
this.results.Rows.Sort('SortMetadata, FoundLine', false);
// Запомним строку поиска в истории.
this.addToHistory(this.form.Query);
if (fromHotKey == true)
{
// Для того чтобы курсор не прыгал при поиске текущего слова,
// тут бы еще добавить чтобы активизировалась именно текущая строка
this.form.Open();
this.form.CurrentControl=this.form.Controls.SearchResults;
if (docRow)
{
var curLineRow = this.getRowForTheCurrentLine(docRow);
if (curLineRow)
this.form.Controls.SearchResults.CurrentRow = curLineRow;
}
}
else if (docRow)
{
if (this.form.TreeView)
this.goToLine(docRow.Rows.Get(0).Rows.Get(0));
else
this.goToLine(docRow.Rows.Get(0));
}
},
showSearchResult: function (docRow, fromHotKey) {
this.showResult(docRow, fromHotKey);
this.expandTree();
if (this.results.Rows.Count() == 0)
{
DoMessageBox('Совпадений не найдено!');
return;
}
this.SetControlsVisible();
},
getRowForTheCurrentLine: function(docRow) {
var twnd = docRow._object.activate();
return docRow.Rows.Find(twnd.GetCaretPos().beginRow, "LineNo", true);
},
getGroupRow: function (docRow, methodData) {
if (!this.form.TreeView || this.re.multiline)
return docRow;
var groupRow = docRow.groupsCache.Get(methodData);
if (!groupRow)
{
groupRow = docRow.Rows.Add();
groupRow.FoundLine = (!methodData.Name)?"":methodData.Name;
groupRow.Method = (!methodData.Name)?"":methodData.Name;
groupRow._object = docRow._object;
if (methodData.IsProc !== undefined)
groupRow.RowType = methodData.IsProc ? RowTypes.ProcGroup : RowTypes.FuncGroup;
groupRow.lineNo = methodData.StartLine + 1;
groupRow._method = methodData;
groupRow.SortMetadata = methodData.SortMetadata;
docRow.groupsCache.Insert(methodData, groupRow);
}
return groupRow;
},
addSearchResult : function (docRow, line, lineNo, matches, methodData) {
var groupRow = this.getGroupRow(docRow, methodData);
var resRow = groupRow.Rows.Add();
resRow.FoundLine = line;
resRow.lineNo = lineNo;
resRow._object = docRow._object;
if(undefined != methodData)
resRow.Method = methodData.Name;
resRow._method = methodData;
resRow._match = matches
if (this.form.WholeWords)
resRow.ExactMatch = matches[0].replace(/^[^\w\dА-я]/, '').replace(/[^\w\dА-я]$/, '');
else
resRow.ExactMatch = matches[0];
},
goToLine : function (row) {
this.form.Controls.SearchResults.CurrentRow = row;
// Откроем и/или активируем окно объекта, в котором выполнялся поиск.
var targetWindow = row._object.activate();
if (!targetWindow.IsActive())
{
DoMessageBox("Окно, для которого выполнялся поиск, было закрыто!\nОкно поиска с результатами стало не актуально и будет закрыто.");
this.clearSearchResults();
this.Close();
return;
}
// Найдем позицию найденного слова в строке.
//debugger
var lineStart = row.LineNo, colStart, lineEnd = lineStart, colEnd
if(row.ExactMatch.substr(row.ExactMatch.length - 1) == '\n')
{
// результат многострочного поиска
var text = row._match.realResult
colStart = row._match.index + 1
colEnd = colStart
for(var k = 0; k < text.length; k++)
{
if(text.charAt(k) == '\n')
{
lineEnd++
colEnd = 1;
}
else
colEnd++
}
}
else
{
var searchPattern = this.form.WholeWords ? "(?:[^\\w\\dА-я]|^)" + row.ExactMatch + "([^\\w\\dА-я]|$)" : StringUtils.addSlashes(row.ExactMatch);
var re = new RegExp(searchPattern, 'g');
var matches = re.exec(row.FoundLine);
colStart = 1;
if (matches)
{
colStart = re.lastIndex - row.ExactMatch.length + 1;
if (this.form.WholeWords && matches.length > 1)
colStart -= matches[1].length;
}
colEnd = colStart + row.ExactMatch.length
}
// Установим выделение на найденное совпадение со строкой поиска.
targetWindow.SetCaretPos(lineStart, colEnd);
targetWindow.SetSelection(lineStart, colStart, lineEnd, colEnd);
},
moveRowCursor : function (forward) {
if (!this.results.Rows.Count())
return;
var row = this.form.Controls.SearchResults.CurrentRow;
if (!row)
{
row = this.results.Rows.Get(0).Get(0);
if (this.form.TreeView)
row = row.Rows.Get(0);
this.goToLine(row);
return;
}
if (forward)
{
if (row.RowType == RowTypes.SearchResult)
{
while (row)
{
var rows = row.Parent ? row.Parent.Rows : this.results.Rows;
var index = rows.IndexOf(row);
if (index < rows.Count() - 1)
{
row = rows.Get(index + 1);
break;
}
if (!row.Parent)
break;
row = row.Parent;
}
}
while (row.Rows.Count() > 0)
row = row.Rows.Get(0);
}
else
{
if (row.RowType == RowTypes.SearchResult)
{
while (row)
{
var rows = row.Parent ? row.Parent.Rows : this.results.Rows;
var index = rows.IndexOf(row);
if (index > 0)
{
row = rows.Get(index - 1);
break;
}
if (!row.Parent)
break;
row = row.Parent;
}
}
while (row.Rows.Count() > 0)
row = row.Rows.Get(row.Rows.Count() - 1);
}
this.goToLine(row);
},
clearSearchResults : function () {
this.results.Rows.Clear();
},
setDefaultSearchQuery : function () {
this.form.CurrentControl=this.form.Controls.Query;
},
addToHistory : function (query) {
if (!query)
return;
// Добавляем в историю только если такой поисковой строки там нет.
var history = this.form.SearchHistory;
if (history.FindByValue(query))
return;
if (history.Count())
history.Insert(0, query);
else
history.Add(query);
// Не позволяем истории расти более заданной глубины.
while (history.Count() > this.form.HistoryDepth)
history.Delete(history.Count() - 1);
},
getRegExpEditorScriptPath : function () {
var mainFolder = profileRoot.getValue("Snegopat/MainFolder");
var scriptPath = mainFolder + "scripts\\RegExpEditor.js";
var f = v8New('File', scriptPath);
if (f.Exist())
return scriptPath;
return '';
},
Form_OnOpen : function () {
if (!this.getRegExpEditorScriptPath())
this.form.Controls.Query.ChoiceButton = false;
this.SetControlsVisible();
},
Form_OnClose : function () {
this.saveSettings();
},
CmdBar_BtPrev : function (control) {
this.moveRowCursor(false);
},
CmdBar_BtNext : function (control) {
this.moveRowCursor(true);
},
Query_OnChange : function (control) {
if (this.form.Query != '')
this.searchActiveDoc();
},
Query_StartListChoice : function (control, defaultHandler) {
control.val.ChoiceList = this.form.SearchHistory;
},
BtSearch_Click : function (control) {
if (this.form.Query == '')
{
DoMessageBox('Не задана строка поиска');
return;
}
this.searchActiveDoc();
},
CmdBarOptions_BtAbout : function (control) {
RunApp('http://snegopat.ru/scripts/wiki?name=extSearch.js');
},
SearchResults_Selection : function (control, selectedRow, selectedCol, defaultHandler) {
this.goToLine(selectedRow.val);
defaultHandler.val = false; // Это для того чтобы после нажатия на строку курсор не уходит с табличного поля, и при новой активизации формы можно было курсором посмотреть другие значения
},
beforeExitApp : function () {
this.watcher.stopWatch();
},
IsRegExp_OnChange : function(Элемент) {
if (this.form.IsRegExp)
this.form.WholeWords = false;
this.SetControlsVisible()
},
WholeWords_OnChange : function(Элемент) {
if (this.form.WholeWords)
this.form.IsRegExp = false;
this.SetControlsVisible();
},
Query_StartChoice : function (Control, DefaultHandler) {
var reEditorPath = this.getRegExpEditorScriptPath();
if (reEditorPath)
{
DefaultHandler.val = false;
reEditorAddin = stdlib.require(reEditorPath);
if (reEditorAddin)
{
this.form.IsRegExp = true;
var reEditor = reEditorAddin.CreateRegExpEditor();
reEditor.open(Control.val);
}
}
},
SearchResults_OnRowOutput : function (Control, RowAppearance, RowData) {
var cell = RowAppearance.val.Cells.FoundLine;
switch (RowData.val.RowType)
{
case RowTypes.FuncGroup:
cell.SetPicture(this.Icons.Func);
break;
case RowTypes.ProcGroup:
cell.SetPicture(this.Icons.Proc);
break;
case RowTypes.SearchDoc:
RowAppearance.val.Cells.LineNo.SetText('');
RowAppearance.val.Font = this.SearchDocRowFont;
RowAppearance.val.TextColor = WebColors.DarkBlue;
break;
default:
break;
}
if (RowData.val._method && RowData.val._method.IsProc !== undefined)
RowAppearance.val.Cells.Method.SetPicture(RowData.val._method.IsProc ? this.Icons.Proc : this.Icons.Func);
},
switchView : function (setTreeView) {
var results = this.results.Copy();
this.clearSearchResults();
for (var docRowIx = 0; docRowIx < results.Rows.Count(); docRowIx++)
{
var oldDocRow = results.Rows.Get(docRowIx);
var docRow = this.results.Rows.Add();
FillPropertyValues(docRow, oldDocRow);
docRow.groupsCache = v8New('Map');
if (setTreeView)
{
for (var i=0; i<oldDocRow.Rows.Count(); i++)
{
var row = oldDocRow.Rows.Get(i);
var groupRow = this.getGroupRow(docRow, row._method);
var resRow = groupRow.Rows.Add();
FillPropertyValues(resRow, row);
}
}
else
{
for (var i=0; i<oldDocRow.Rows.Count(); i++)
{
var groupRow = oldDocRow.Rows.Get(i);
for (var j=0; j<groupRow.Rows.Count(); j++)
{
var row = groupRow.Rows.Get(j);
var resRow = docRow.Rows.Add();
FillPropertyValues(resRow, row);
}
}
}
}
this.expandTree();
this.SetControlsVisible();
},
CmdBar_TreeView : function (Button) {
this.form.TreeView = !this.form.TreeView;
Button.val.Check = this.form.TreeView;
//this.form.Controls.SearchResults.Columns.FoundLine.ShowHierarchy = this.form.TreeView;
this.switchView(this.form.TreeView);
},
CmdBar_ExpandAll : function (Button) {
this.expandTree(false);
},
CmdBar_CollapseAll : function (Button) {
this.expandTree(true);
},
SetControlsVisible : function() {
var ctr = this.form.Controls;
//ctr.SearchResults.Columns.FoundLine.ShowHierarchy = this.form.TreeView;
ctr.CmdBar.Buttons.TreeView.Check = this.form.TreeView;
this.form.Controls.SearchResults.Columns.Method.Visible = !this.form.TreeView;
this.form.Controls.SearchResults.Columns.ExactMatch.Visible = this.form.IsRegExp;
var buttons = this.form.Controls.CmdBar.Buttons;
buttons.ExpandAll.Enabled = this.form.TreeView;
buttons.Actions.Buttons.ExpandAll.Enabled = this.form.TreeView;
buttons.CollapseAll.Enabled = this.form.TreeView;
buttons.Actions.Buttons.CollapseAll.Enabled = this.form.TreeView;
this.form.caption = "Расширенный поиск в модуле";
}
}); // end of ExtSearch class
ExtSearchGlobal = ExtSearch.extend({
settingsRootPath : SelfScript.uniqueName+"Global", // тест, пускай у нас и настройки будут глобальными.
settings : {
pflSnegopat : {
'IsRegExp' : false, // Поиск регулярными выражениями.
'CaseSensetive' : false, // Учитывать регистр при поиске.
'WholeWords' : false, // Поиск слова целиком.
'SearchHistory' : v8New('ValueList'), // История поиска.
'HistoryDepth' : 15, // Количество элементов истории поиска.
'TreeView' : false // Группировать результаты поиска по методам.
}
},
construct : function () {
this._super(true);
this._instance = null;
this.form.КлючСохраненияПоложенияОкна = "extGlobalSearch.js";
this.isGlobalFind = true;
//TODO: признак автомтически назначаемого хоткей, если уже назначен на отмену поиска, автоматом не будет назначаться.
this.dynamicHotKey = true;
for(var i = 0; i < HotKeys.count; i++)
{
var hk = HotKeys.item(i);
Команда = hk.addin + "::" + hk.macros
if (Команда.indexOf("ExtendedSearch::Отменить глобальный поиск")!=-1){
this.dynamicHotKey = false;
break;
}
}
this.expandetRows = {};
this.SetControlsVisible();
//FIXME: вынести в настройку.
this.countRowsInIdleSearch = 25; //Количество объектов поиска в фоне(для слабеньких машин ставим меньше, для формула1 - как удобней)
this.re = new RegExp(/(([а-яa-z0-9]{1,})\s[а-яa-z0-9]{1,})(\.|\:)/i);
this.filterByUUID = null;
ExtSearchGlobal._instance = this;
},
searchByUuid: function(row, sort) {
mdObj = findMdObj(this.currentMdContainer, row.UUID);
if (sort == undefined) sort = 999;
var docRow = null;
if (mdObj){
var obj = this.getWindowObject({
mdObj:mdObj,
mdProp:row.mdProp,
title:row.title});
obj.sort = sort+1;
docRow = this.search(obj, this.re);
}
return docRow;
},
searchInMetadata : function(fromHotKey){
var md = null;
var objTitle = "";
var activeWindow = this.watcher.getActiveTextWindow();
if (!activeWindow) {
} else {
var activeView = activeWindow.GetView();
var obj = this.getWindowObject(activeView);
if (obj!=null){
objTitle = obj.getTitle();
var matches = this.re.exec(objTitle);
if (matches!=null){
objTitle = matches[1];
} else {
if (objTitle.indexOf(":")!=-1){
objTitle = objTitle.substr(0, objTitle.indexOf(":"));
}
}
}
}
md = this.getCurrentMd();
if (!md) return;
this.currentMdContainer = md;
this.clearSearchResults();
this.re = this.buildSearchRegExpObject();
if (!this.re) return;
this.curCaption = windows.caption; //а вдруг, еще кто-то не пользуется configCaption...
this.startGlobalSearch = true;
if (!this.vtMD){
this.vtMD = {};
}
this.reatingMdObjects = {"ОбщийМодуль":2,
"Конфигурация":3,
"ПланОбмена":4,
"ОбщаяФорма":5
};
if (objTitle.length>0){
this.reatingMdObjects[objTitle]=1; //Самый высокий рейтинг...
}
this.readMdToVt(this.currentMdContainer);
this.expandetRows = {};
this.curId = 0;
if (this.dynamicHotKey)
hotkeys.AddHotKey("Ctrl+Shift+BkSpace", "ExtendedSearch", "Отменить глобальный поиск");
events.connect(Designer, "onIdle", this);
//this.showSearchResult(docRow, fromHotKey);
//windows.caption = curCaption;
},
getCurrentMd:function(){
var md ;
if (this.isInCurrentMdConteinerFind ) {
if (!this.activeView){
var activeWindow = this.watcher.getActiveTextWindow();
if (!activeWindow){
} else {
var activeView = activeWindow.GetView();
}
} else {
var activeView = this.activeView;
}
//Определим объект конфигурации по текущему окну.
if (!activeView) {
} else {
if (activeView.mdObj && activeView.mdProp) {
md = activeView.mdObj.container;
} else if (activeView.mdObj) {
md = activeView.mdObj.container;
}
}
}
if (!md) {
md = metadata.current;
}
return md;
},
onIdle:function(){
if (!this.startGlobalSearch) {
windows.caption = this.curCaption;
events.disconnect(Designer, "onIdle", this);
this.showSearchResult(docRow, false);
this.expandetRows = {};
if (this.dynamicHotKey) {
for(var i = 0; i < HotKeys.count; i++)
{
var hk = HotKeys.item(i);
Команда = hk.addin + "::" + hk.macros
if (Команда.indexOf("ExtendedSearch::Отменить глобальный поиск")!=-1){
try {
HotKeys.remove(i);
} catch (e) {}
}
}
}
return;
}
var currentId = this.currentMdContainer.rootObject.id;
if (this.vtMD[currentId].Count()<1) {
this.startGlobalSearch = false;
events.disconnect(Designer, "onIdle", this);
return;
}
var count = 0;
var docRow = null;
while (count < this.countRowsInIdleSearch){
if (this.curId<this.vtMD[currentId].Count()){
//docRow = this.searchByUuid(this.vtMD[currentId][this.curId]);
var currRow = this.vtMD[currentId].Get(this.curId);
docRow = this.searchByUuid(currRow, this.curId);
windows.caption = currRow.mdName;
} else {
this.startGlobalSearch = false;
break;
}
this.curId ++;
count++;
}
this.showSearchResult(null, false);
},
readMdToVt:function(MdContainer){
var currentId = MdContainer.rootObject.id;
if (!this.vtMD[currentId]){
var docRow = null;
//this.vtMD[currentId] = [];
this.vtMD[currentId]=v8New("ValueTable");
this.vtMD[currentId].Columns.Add("UUID");
this.vtMD[currentId].Columns.Add("mdProp");
this.vtMD[currentId].Columns.Add("mdName");
this.vtMD[currentId].Columns.Add("title");
this.vtMD[currentId].Columns.Add("sortTitle");
this.vtMD[currentId].Columns.Add("sort");
this.vtMD[currentId].Columns.Add("LineNumber");
var es = this;
//Реквизиты пропустим
var ignoredMdClass = {
"Реквизиты":"",
"Макеты" : "" ,
"ОбщиеКартинки" : "" ,
"Элементы стиля" : "" ,
"Подсистемы" : "" ,
"Языки" : "" ,
"Стили" : "" ,
"Интерфейсы" : "" ,
"ПараметрыСеанса" : "" ,
"Роли" : "" ,
"ОбщиеМакеты" : "" ,
"КритерииОтбора" : "" ,
"ОбщиеРеквизиты" : "" ,
"ТабличныеЧасти" : "" ,
"Параметры" : ""
};
var LineNumber = 0; //Для сортировки модулей функций по порядку обхода, а не по алфавиту.
(function (mdObj){
if (!es.startGlobalSearch) {return}
var mdc = mdObj.mdclass;
function getMdName(mdObj) {
if (mdObj.parent && mdObj.parent.mdClass.name(1) != 'Конфигурация')
return getMdName(mdObj.parent) + '.' + mdObj.mdClass.name(1) + ' ' + mdObj.name;
var cname = mdObj.mdClass.name(1);
return (cname ? cname + ' ' : '') + mdObj.name;
}
var mdName = getMdName(mdObj)
for(var i = 0, c = mdc.propertiesCount; i < c; i++){
var mdProp = mdc.propertyAt(i);
var mdPropName = mdc.propertyAt(i).name(1);
if (mdObj.isPropModule(mdProp.id)){
//var row = {UUID : mdObj.id}
var row = es.vtMD[currentId].Add();
row.UUID = mdObj.id;
row.mdProp = mdProp;
row.mdName = mdName;
LineNumber++;
var title = mdName + ': ' + mdPropName;
row.title = title;
row.sort = 9;
row.LineNumber = LineNumber;
var matches;
var re = new RegExp(/(([а-яa-z0-9]{1,})\s[а-яa-z0-9]{1,})(\.|:)/i);
matches = re.exec(mdName);
if (matches!=null){
row.sortTitle = matches[1];
if (!es.reatingMdObjects[matches[1]]){
if (!es.reatingMdObjects[matches[2]]) {
row.sort = 9;
} else {
row.sort = es.reatingMdObjects[matches[2]];
}
} else {
row.sort = es.reatingMdObjects[matches[1]];
}
}
}
}
// Перебираем классы потомков (например у Документа это Реквизиты, ТабличныеЧасти, Формы)
for(var i = 0; i < mdc.childsClassesCount; i++)
{
var childMdClass = mdc.childClassAt(i)
if (!(ignoredMdClass[childMdClass.name(1, true)]==undefined)){
continue;
}
// Для остального переберем потомков этого класса.
for(var chldidx = 0, c = mdObj.childObjectsCount(i); chldidx < c; chldidx++){
var childObject = mdObj.childObject(i, chldidx);
arguments.callee(childObject);
}
}
})(MdContainer.rootObject)
} else {
for (var key in this.reatingMdObjects){
if (this.reatingMdObjects[key]<2) {
var filter = v8New("Structure");
filter.Insert("sort", 1);
var findRows = this.vtMD[currentId].FindRows(filter);
if (findRows.Count()>0){
for (var i=0; i<findRows.Count(); i++){
var currRow = findRows.Get(i);
if (currRow.sortTitle != key){
currRow.sort = 9;
}
}
}
var filter = v8New("Structure");
filter.Insert("sortTitle", key);
var findRows = this.vtMD[currentId].FindRows(filter);
if (findRows.Count()>0){
for (var i=0; i<findRows.Count(); i++){
var currRow = findRows.Get(i);
if (currRow.sortTitle != key){
currRow.sort = (!this.reatingMdObjects[key]) ? 9: this.reatingMdObjects[key];
}
}
}
}
}
}
if (this.filterByUUID){
var arrayToFilter = v8New('Array');
var firstElement = false;
for (var k in this.filterByUUID){
firstElement = true;
var filter = v8New("Structure");
filter.Insert("UUID", k);
var findRows = this.vtMD[currentId].FindRows(filter);
if (findRows.Count()>0){
for (var i=0; i<findRows.Count(); i++){
arrayToFilter.Add(findRows.Get(i));
}
}
}
if (firstElement)
this.vtMD[currentId] = this.vtMD[currentId].Copy(arrayToFilter);
}
this.vtMD[currentId].Sort("sort, LineNumber, title");
},
Query_OnChange : function(Control){
return;
},
BtSearch_Click : function (control) {
if (this.form.Query == '')
{
DoMessageBox('Не задана строка поиска');
return;
}
this.searchInMetadata(true);
},
SetControlsVisible : function(){
this._super();
if (this.isGlobalFind){
this.form.caption = "Расширенный поиск в модуле (глобальный)";
}
},
showSearchResult: function (docRow, fromHotKey) {
this.showResult(docRow, fromHotKey);
this.expandTree();
},
expandTree : function (collapse) {
var tree = this.form.Controls.SearchResults;
for (var i=0; i < this.results.Rows.Count(); i++)
{
var docRow = this.results.Rows.Get(i);
if (this.form.TreeView)
{
for (var j=0; j < docRow.Rows.Count(); j++)
{
var row = docRow.Rows.Get(j);
if (this.expandetRows[""+row.LineNo+row.FoundLine]){
continue;
}
collapse ? tree.Collapse(row) : tree.Expand(row, true);
if (this.startGlobalSearch){
this.expandetRows[""+row.LineNo+row.FoundLine] = "1";
}
}
}
else
{
if (this.expandetRows[""+docRow.LineNo+docRow.FoundLine]){
continue;
}
collapse ? tree.Collapse(docRow) : tree.Expand(docRow, true);
if (this.startGlobalSearch){
this.expandetRows[""+docRow.LineNo+docRow.FoundLine] = "1";
}
}
}
}
})
////} ExtSearch
////////////////////////////////////////////////////////////////////////////////////////
////{ Вспомогательные объекты.
////
MdObject = stdlib.Class.extend({
construct: function (obj, prop, title) {
this.obj = obj;
this.prop = prop;
this.title = title;
},
getText: function() {
return this.obj.getModuleText(this.prop.id);
},
activate: function() {
this.obj.openModule(this.prop.id);
return GetTextWindow();
},
getTitle: function() {
if (!this.title)
{
function getMdName(mdObj) {
if (mdObj.parent && mdObj.parent.mdClass.name(1) != 'Конфигурация')
return getMdName(mdObj.parent) + '.' + mdObj.mdClass.name(1) + ' ' + mdObj.name;
var cname = mdObj.mdClass.name(1);
return (cname ? cname + ' ' : '') + mdObj.name;
}
this.title = getMdName(this.obj) + ': ' + this.prop.name(1);
}
return this.title;
}
});
TextDocObject = stdlib.Class.extend({
construct: function (txtDoc, title) {
this.obj = txtDoc;
this.title = title;
},
getText: function() {
return this.obj.GetText();
},
activate: function() {
this.obj.Show();
return GetTextWindow();
},
getTitle: function() {
if (!this.title)
this.title = this.obj.UsedFileName;
return this.title;
}
});
function findMdObj(currentmd, uuid)
{
if(uuid == currentmd.rootObject.id)
return currentmd.rootObject
return currentmd.findByUUID(uuid);
}
////
////} Вспомогательные объекты.
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
////{ TextWindowsWatcher - отслеживает активизацию текстовых окон и запоминает последнее.
////
TextWindowsWatcher = stdlib.Class.extend({
construct : function() {
this.timerId = 0;
this.lastActiveTextWindow = null;
this.startWatch();
},
getActiveTextWindow : function () {
if (this.lastActiveTextWindow && this.lastActiveTextWindow.IsActive())
return this.lastActiveTextWindow;
return null;
},
startWatch : function () {
if (this.timerId)
this.stopWatch();
this.timerId = createTimer(500, this, 'onTimer');
},
stopWatch : function () {
if (!this.timerId)
return;
killTimer(this.timerId);
this.timerId = 0;
},
onTimer : function (timerId) {
var wnd = GetTextWindow();
if (wnd)
this.lastActiveTextWindow = wnd;
else if (this.lastActiveTextWindow && !this.lastActiveTextWindow.IsActive())
this.lastActiveTextWindow = null;
}
}); // end of TextWindowsWatcher class
//} TextWindowsWatcher
////////////////////////////////////////////////////////////////////////////////////////
////{ StartUp
////
function GetExtSearch() {
if (!ExtSearch._instance)
new ExtSearch();
return ExtSearch._instance;
}
function GetExtSearchGlobal() {
if (!ExtSearchGlobal._instance)
new ExtSearchGlobal();
return ExtSearchGlobal._instance;
}
events.connect(Designer, "beforeExitApp", GetExtSearch());
events.connect(Designer, "beforeExitApp", GetExtSearchGlobal());
////}