Wiki

Clone wiki

extDiff-snegopat / extSyntaxCheck

Альтернативный вывод информации об ошибка при синтаксической проверки.

Введение

У меня стоит автоматическая проверка синтаксиса для сервера/внешнего соединения/клиента и в случаии ошибки или опечатки при проверке синтаксиса появляется сразу 3 сообщения с одной и тоей же ошибкой, что очень неудобно.

Попытаемся исправить. Для этого необходимо перехватить сообщение об ошибки и вывести в свою форму, где уже сгрупируем так как нам удобно.

  1. Идем на сайт снегопата и благодарим Артура Аюханова за описание событий . Как можно посмотреть, есть событие onMessage к которому можно попробовать присоединиться, с одним едениственным параметром.
  2. В первый попавшийся скрипт подключаем событие и ставим точку останова. ключевое слово debbuger Как видим перехват сообщения есть, так что можно приступать к созданию своего скрипта по показу ошибок и нашей формы со своими плюшками.

  3. Приступим к созданию скрипта. Назовем его extSyntaxCheck.

    #!javascript
    
    $engine JScript
    $uname extSyntaxCheck
    $dname Расширенние сообщений об ошибках .
    $addin stdlib
    $addin stdcommands
    
    // (c) Сосна Евгений <shenja@sosna.zp.ua>
    
    stdlib.require('TextWindow.js', SelfScript);
    stdlib.require('ScriptForm.js', SelfScript);
    stdlib.require('log4js.js', SelfScript);
    
    Подключим необходимые скрипты. Стоит обратить внимание на ScriptForm.js, замечательная библиотека упрощающая работу с формой и настройками для скирпта. Без TextWindow.js не обойдется ни один скрипт, желающий работать с текстовым окном. Ну и log4js.js для удобного логгирования, применяю в основном когда хочеться без отладки понять что выполняет скрипт.

#!javascript

var logger = Log4js.getLogger(SelfScript.uniqueName);
var appender = new Log4js.BrowserConsoleAppender();
appender.setLayout(new Log4js.PatternLayout(Log4js.PatternLayout.TTCC_CONVERSION_PATTERN));
logger.addAppender(appender);
logger.setLevel(Log4js.Level.ERROR);
Определяем переменную logger , устанавливаем формат вывода информации и с помощью logger.setLevel(Log4js.Level.ERROR); устанавливаем уровень вывода сообщений, только для ошибок.

Напомним себе задачи и цели данного скрипта

Скрипт перехватывает сообщения о синтаксической проверки и выводит само сообщение только один раз, а вид проверки толи на сервере или же для внешнего соединения будет выводить отдельным списком. Скрипт должен иметь настройку, пока вижу только булевый флаг перехватывать/не перехватывать и возможность указания данной настройки. Для таких простых настроек можно обойдить простым MessageBox с варинатами ответов, но нам интересно ведь нарисовать совю форму. В статье Александра Кунташова подробно описано создание формы и подключение обработчиков событий для элементов форм и примеры сохранения и восстановления настроек, но мы пойдем еще более простым путем - будем использовать уже готовые библиотеки и одна из них это ScriptForm.js . На базе ScriptForm создадим объект, для которого определим необходимые параметры для сохранения/восстановеления настроек.

#!javascript
ExtSyntaxCheck = ScriptForm.extend({

    settingsRootPath : SelfScript.uniqueName,

    settings : {
        pflSnegopat : {
            'cathSyntaxCheck': false // Перехватывать комманду синтаксической проверки.
        }
    },

    construct : function () {

        this._super(SelfScript.fullPath.replace(/.js$/, '.ssf')); //Загрузим форму с настройками, форма должна называться так же как и скрипт, только с расширением ssf

        this.loadSettings(); //Загрузим сохраненные настройки. 

        ExtSyntaxCheck._instance = this;
     }, 

     loadSettings: function(){
                this._super();

    },
    Ok_Click:function(Button){
        this.saveSettings();
                this.loadSettings();
        this.form.Close();
    }, 

    Close_Click:function(Button){
        this.form.Close();
    }

})


function GetExtSyntaxCheck() {
    if (!ExtSyntaxCheck._instance)
        new ExtSyntaxCheck();

    return ExtSyntaxCheck._instance;
}

GetExtSyntaxCheck(); 
Пройдемся чуть подробней. С помощью данной вызова мы создадим полную копию объекта ScriptForm
ExtSyntaxCheck = ScriptForm.extend({})
Оперделим наименование для пути сохранения настроек данного скрипта, можна как ручками написать любое в латинице, или же использовать наименование скрипта, что по мне так удобней.
settingsRootPath : SelfScript.uniqueName,
Определим наименования настроек и стандартные значения для них. Для этого переопредлим переменную settings своими настройками. Будем хранить только одно булево значение, в целом для базы: за это отвечает наименование настройки pflSnegopat .
 settings : {
        pflSnegopat : {
            'cathSyntaxCheck': false // Перехватывать комманду синтаксической проверки.
        }
    },
Функция construct будет вызываться один раз при инициализации объекта. Обычно читаем настройки, определяем переменные.
        this._super(SelfScript.fullPath.replace(/.js$/, '.ssf')); //Загрузим форму с настройками, форма должна называться так же как и скрипт, только с расширением ssf
        this.loadSettings(); //Загрузим сохраненные настройки. 
        ExtSyntaxCheck._instance = this;
     }, 
Как видим инициализируем родительский метод, this._super() и передаем полный путь к файлу с формой. Функция loadSettings сейчас никакой смысловой нагрузки не несет, обычня я переопределяю ее, если мне необходимо в настройках снегопата сохранять таблицу значений, тогда при сохранении настроек я таблицу преобразую в строку (ЗначениеВСтрокуВнутр), а при восстановлении настроек в попытке обратно в из строки в таблицу значений.

Интересны строки кода, для переопределения обработчиков событий, если открыть сам файл формы, то можно увидеть, что никаких обработчиков событий я не подключал к кнопкам Ok и закрыть.

#!javascript

    Ok_Click:function(Button){
        this.saveSettings();
        this.loadSettings();
        this.form.Close();
    }, 

    Close_Click:function(Button){
        this.form.Close();
    }
Для того, что бы автоматически подхватились кнопки достаточно следовать правилу в названиях функций ИмяКонтрола _Имя собыитя . В данном случаии для кнопки с именем Ok , я переопределил обарботчик события Click и при вызове процедуры construct , данный обработчик будет подключен к кнопке. Практический примеры переопределения можно посмотреть в скриптах extSearch , ну и практически во всех где используется библиотека ScriptForm.js

Итак полностью скрипт будет выглядить так.

#!javascript

$engine JScript
$uname extSyntaxCheck
$dname Расширенние сообщений об ошибках .
$addin stdlib
$addin stdcommands

// (c) Сосна Евгений <shenja@sosna.zp.ua>

stdlib.require('TextWindow.js', SelfScript);
stdlib.require('ScriptForm.js', SelfScript);
stdlib.require('log4js.js', SelfScript);

var logger = Log4js.getLogger(SelfScript.uniqueName);
var appender = new Log4js.BrowserConsoleAppender();
appender.setLayout(new Log4js.PatternLayout(Log4js.PatternLayout.TTCC_CONVERSION_PATTERN));
logger.addAppender(appender);
logger.setLevel(Log4js.Level.ERROR);

SelfScript.self['macrosНастройка'] = function() {
    extSyntaxCheck = GetExtSyntaxCheck();
    extSyntaxCheck.show();
}

ExtSyntaxCheck = ScriptForm.extend({

    settingsRootPath : SelfScript.uniqueName,

    settings : {
        pflSnegopat : {
            'cathSyntaxCheck': false // Перехватывать комманду синтаксической проверки.
        }
    },

    construct : function () {

        this._super(SelfScript.fullPath.replace(/.js$/, '.ssf')); //Загрузим форму с настройками, форма должна называться так же как и скрипт, только с расширением ssf
        this.loadSettings(); //Загрузим сохраненные настройки. 

        ExtSyntaxCheck._instance = this;
    }, 

    loadSettings: function(){
        this._super();

    },

    Ok_Click:function(Button){
        this.saveSettings();
        this.loadSettings();
        this.form.Close();
    }, 

    Close_Click:function(Button){
        this.form.Close();
    }

})

function GetExtSyntaxCheck() {
    if (!ExtSyntaxCheck._instance)
        new ExtSyntaxCheck();

    return ExtSyntaxCheck._instance;
}

GetExtSyntaxCheck();

Мы определили макрос для вызова настроек, форма открываетса, настройка сохраняется.

Приступим к самому интиресному. Перехват сообщения. Для этого необходимо подключиться к событию onMessage. Добавим в onMessage и подключение к событию в loadSettings. Для того что бы событие onMessage не отрабатывало все подряд сообщения, будем подключаться только тогда когда пользователь вызовет проверку синтаксиса. В простом условии определяем установленна ли настройка перехвата сообщений, если установленна тогда подклчюаемся к событию проверки. Примеры работы с событиями вызова команд 1С можно посмотреть в traymessage.js.

#!javascript

    loadSettings: function(){
        this._super();

        if (this.form.cathSyntaxCheck) { 
            //Подключаемся к команде проверки текста 
            logger.debug('loadSettings addHandler')
             stdcommands.Frntend.SyntaxCheck.addHandler(this, "onSyntaxCheck");
        } else {
            logger.debug('loadSettings delHandler')
            try {
                stdcommands.Frntend.SyntaxCheck.delHandler(this, "onSyntaxCheck");
            } catch(e) {
                logger.debug(e.description);
            }
        }
    },
Указав в stdcommands.Frntend.SyntaxCheck.addHandler(this, "onSyntaxCheck"); this мы определили название функции которая будет вызываться при событии проверки синтаксиса. При окончании проверки мы очищаем список старых ошибок this.errors = {}; и подключаемся к событию onMessage
#!javascript
            logger.debug('onSyntaxCheck connect')
            events.connect(Designer, "onMessage", this);
когда комманда проверки отработает, проверяем состояние, тогда отключимся от события:

#!javascript

if(!cmd.isBefore)
 { 
            logger.debug('onSyntaxCheck disconnect')
            this.errors = {};

            try {
                    events.disconnect(Designer, "onMessage", this);

                 } catch (e) { }
 }
Итак полностью функция onSyntaxCheck будет выглядить так:

#!javascript

    onSyntaxCheck : function (cmd){
        logger.debug('onSyntaxCheck ')
        if(!cmd.isBefore)
        { 
            logger.debug('onSyntaxCheck disconnect')
            this.errors = {};

            try {
                    events.disconnect(Designer, "onMessage", this);

                 } catch (e) { }

        } else {
            logger.debug('onSyntaxCheck connect')
            events.connect(Designer, "onMessage", this);
        }
        logger.debug('onSyntaxCheck end')
    },

Проверка об ошибках

Для начала определим саму функцию onMessage и будем просто дублировать сообщения.

#!javascript

onMessage:function(param){
        text = param.text;
        if (!text.length)
            return

        Message(''+text)
Подключили, запустили проверку ииии, ушли в бесконечный цикл, т.к. нам надо или фильтровать сообщения или же выводить после того как отключимся от подписки. Для фильтра будем использовать регулярные сообщения, если подошло, тогда сообщение не выводим, а запоминаем его в массиве, после проверки выведем свое сообщение.

#!javascript

    onMessage:function(param){
        text = param.text;
        if (!text.length)
            return

        this.RE = new RegExp("^(\\{.{1,}\\}\:.{1,}\)$", "mig"); 
        if ((Matches = this.RE.exec(text)) != null){
            param.cancel = true;
            str = Matches[1];
            if (!this.errors[str]){
                this.errors[str] = text;
            } else {
                this.RE_ERROR_TEXT = new RegExp("\\s\\((Проверка\\:\\s.{1,})\\)$", "gim");
                param.cancel = true;
                if ((Matches_error = this.RE_ERROR_TEXT.exec(text)) != null){
                    str_error = Matches_error[0];
                    this.errors[str] = this.errors[str]+' '+str_error;
                }
            }
        }
    },

Как видно, по первому регулярном выражению фильтруем заголовок, проверяем если уже существует такое сообщение, тогда в стек для вывода не добавляем, а пытаемся найти какая проверка сработала с помощью другого регулярного выражения. Например для строки

{ВнешняяОбработка.ВыгрузкаЗагрузкаДанныхXML.МодульОбъекта(16,1)}: Объявления переменных должны быть расположены в начале модуля, процедуры или функции
<<?>>Перем ИспользующиеИтоги; (Проверка: Сервер) 
Первое регулярное выражение найдет
{ВнешняяОбработка.ВыгрузкаЗагрузкаДанныхXML.МодульОбъекта(16,1)}: Объявления переменных должны быть расположены в начале модуля, процедуры или функции
а второе регулярное выражение:
(Проверка: Сервер) 

В результате, будем к тексту просто добавлять режимы проверки, и итоговая строка для вывода пользователю получиться:

{ВнешняяОбработка.ВыгрузкаЗагрузкаДанныхXML.МодульОбъекта(16,1)}: Объявления переменных должны быть расположены в начале модуля, процедуры или функции
<<?>>Перем ИспользующиеИтоги; (Проверка: Сервер)  (Проверка: Внешнее соединение)  (Проверка: Толстый клиент (обычное приложение))

Осталось дело за малым вывести эти ошибки пользователю. Для этого в функцию onSyntaxCheck добавим вывод сообщений уже после того как отключимся от события onMessage. В простейшем случаи достаточно проверить есть ли у нас в стеке записи с ошибками и вывести их как сообщения.

#!javascript

for (var k in this.errors){
      hasErrors = true;
      text = this.errors[k];
      Message(''+text);
}

Updated