﻿$engine JScript
$uname author
$dname Авторский комментарий
$addin global
$addin stdlib

stdlib.require("TextWindow.js", SelfScript);
global.connectGlobals(SelfScript);

////////////////////////////////////////////////////////////////////////////////////////
////{ Cкрипт "Авторский комментарий" (author.js) для проекта "Снегопат"
////
//// Описание: Реализует возможности разметки кода по признакам модифицированности 
//// со указанием реквизитов автора.
////
//// Автор: Александр Кунташов <kuntashov@gmail.com>, http://compaud.ru/blog
////}
////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////
////{ Макросы
////

SelfScript.self['macrosМаркер "Добавлено"'] = function() {
    addMarker(MarkerTypes.ADDED);
}

SelfScript.self['macrosМаркер "Изменено"'] = function() {
    addMarker(MarkerTypes.CHANGED);
}

SelfScript.self['macrosМаркер "Удалено"'] = function() {
    addMarker(MarkerTypes.REMOVED);
}

SelfScript.self['macrosНастройка'] = function() {
    // form - неявно определяемая глобальная переменная.
    form = loadScriptForm(SelfScript.fullPath.replace(/js$/, 'ssf'), SelfScript.self);
    form.DoModal();
    form = null;
}

/* Возвращает название макроса по умолчанию - вызывается, когда пользователь 
дважды щелкает мышью по названию скрипта в окне Снегопата. */
function getDefaultMacros() {
    return 'Настройка';
}

////} Макросы

var MarkerTypes = {
    ADDED: "МаркерДобавлено",
    REMOVED: "МаркерУдалено",
    CHANGED:"МаркерИзменено"
};

var MarkerFormatStringParameters = {};
var pflAuthorJs = 'Авторский комментарий/Настройки';

function addMarker(markerType) {

    var w = GetTextWindow();
    if (!w) return;
            
    var sel = w.GetSelection();
    if (sel.beginRow == sel.endRow)
    {    
        // Однострочник.        
        var line = w.GetLine(sel.beginRow);
        var code = markLine(markerType, line);        
        w.ReplaceLine(sel.beginRow, code);
    }
    else 
    {
        // Блок кода.
        var endRow = sel.endCol > 1 ? sel.endRow : sel.endRow - 1;
        var block = w.Range(sel.beginRow, 1, endRow).GetText();        
        var code = markBlock(markerType, block);        
        w.Range(sel.beginRow, 1, endRow).SetText(code);
    }    
}

function getSignature() {
    var fmt = Settings['ФорматПодписи'];
    var ptn = /%(.+?)(?:#(.+)){0,1}%/ig;
    return fmt.replace(ptn, function (match, p1, p2, offset, s) {
        // p1 - имя управляющей конструкции.
        // p2 - параметр управляющей конструкции (для ДатаВремя).
        return MarkerFormatStringParameters[p1].call(null, p2);
    });
}

function getStartComment(markerType) {
    return "//" + Settings[markerType] + " " + getSignature();
}

function getEndComment() {

    var endComment = "//" + Settings["ЗакрывающийМаркерБлока"];
    
    if (!Settings["НеДобавлятьСигнатуруПослеЗакрывающегоМаркера"])
        endComment += " " + getSignature();

    return endComment;
}

function markLine(markerType, line) {

    // Удалим концевые пробелы в строке.
    var code = line.replace(/(.+?)\s*$/, "$1");
    
    switch (markerType) 
    {
    case MarkerTypes.ADDED:
        // Добавляем в хвост подпись.
        code = code + getStartComment(markerType);
        break;
        
    case MarkerTypes.REMOVED:
        // Закомментируем строку и в хвост добавим подпись.
        code = commentLine(code) + getStartComment(markerType);
        break;
        
    case MarkerTypes.CHANGED:
        // Маркер "Изменено" для однострочника такой же как и для блока.
        var indent = StringUtils.getIndent(code);
        code = indent + getStartComment(markerType) + "\n";
        code += prepareChangedBlock(line, indent) + "\n";            
        code += indent + getEndComment() + "\n";            
        break;
    }
        
    return code;
}

function markBlock(markerType, block) {
    
    var indent = StringUtils.getIndent(block);
    var code = indent + getStartComment(markerType) + "\n";
    
    switch (markerType) 
    {
    case MarkerTypes.ADDED:
        code += block + "\n"    
        break;
        
    case MarkerTypes.REMOVED:
        code += commentBlock(block, indent) + "\n";
        break;
        
    case MarkerTypes.CHANGED:
        code += prepareChangedBlock(block, indent) + "\n"
        break;
    }
    
    //Комментарий окончания изменений.
    code += indent + getEndComment();
   
    return code;
}

function prepareChangedBlock(block, indent) {
    
    var code = '';    
    if (!Settings["НеОставлятьКопиюКодаПриЗамене"]) 
    {
        code += commentBlock(block, indent) + "\n";
        
        if (Settings["РазделительКодаПриЗамене"])
            code += indent + "//" + Settings["РазделительКодаПриЗамене"] + "\n";                
    }
    
    code += block;
    
    return code;
}

function commentLine(line, indent) {
    // Комментарий поставим после отступа.
    if (!indent) 
        indent = '\\s+';
    return line.replace(new RegExp('^(' + indent + ')(.*)'), "$1//$2");
}

function commentBlock(block, indent) {
    var lines = StringUtils.toLines(block);
    for(var i=0; i<lines.length; i++)
        lines[i] = commentLine(lines[i], indent);
    return StringUtils.fromLines(lines);
}

function getSettings() {

    var s = v8New("Структура");
    
    /* Настройки для подписи. Общий формат подписи:
       
       //<Маркер> <Подпись>
       ... содержимое блока ...
       //<ЗакрывающийМаркерБлока> <Подпись>
       
    Для однострочника не используется завершающая часть комментария, 
    т.к. однострочник добавляется в конец строки. */
    
    // Настройки по умолчанию.
    s.Вставить("ФорматПодписи", "%ИмяПользователяОС% %ДатаВремя#ДФ=dd.MM.yyyy%");
    s.Вставить("МаркерДобавлено", "Добавлено:");
    s.Вставить("МаркерУдалено", "Удалено:");
    s.Вставить("МаркерИзменено", "Изменено:");
    s.Вставить("ЗакрывающийМаркерБлока", "/");
    s.Вставить("РазделительКодаПриЗамене", "---- Заменено на: ----");    
    // Дополнительные настройки:
    s.Вставить("НеОставлятьКопиюКодаПриЗамене", false);
    s.Вставить("НеДобавлятьСигнатуруПослеЗакрывающегоМаркера", false);
    
    profileRoot.createValue(pflAuthorJs, s, pflSnegopat)    
    s = profileRoot.getValue(pflAuthorJs);
    
    return s;
}

function parseTpl() {
    var a = [];    
    for (var i=0; i<arguments.length;  i++)
        a.push(arguments[i]);        
    return snegopat.parseTemplateString('<?"", ' + a.join(',') + '>');
}

function addFormatStringParam(name, code) {
    var paramGetter = function (p) {
        return eval(code);
    }
    MarkerFormatStringParameters[name] = paramGetter;
}

function addToSignatureFormat(form, paramName) {
    if (!form.ФорматПодписи.match(/^\s+$/))
        form.ФорматПодписи += ' ';
    form.ФорматПодписи += '%' + paramName + '%';
}

//{ Обработчики элементов управления формы
function ПриОткрытии () {
    ЗаполнитьЗначенияСвойств(form, Settings);
}

function КнопкаОкНажатие (Элемент) {
    ЗаполнитьЗначенияСвойств(Settings, form);
    profileRoot.setValue(pflAuthorJs, Settings);
    form.Close();
}

function КнопкаОтменаНажатие (Элемент) {
    form.Close();
}

function НадписьИмяПользователяНажатие (Элемент) {
    addToSignatureFormat(form, Элемент.val.Заголовок);
}

function НадписьПолноеИмяПользователяНажатие (Элемент) {
    addToSignatureFormat(form, Элемент.val.Заголовок);
}

function НадписьИмяПользователяХранилищаКонфигурацииНажатие (Элемент) {
    addToSignatureFormat(form, Элемент.val.Заголовок);
}

function НадписьИмяПользователяОСНажатие (Элемент) {
    addToSignatureFormat(form, Элемент.val.Заголовок);
}

function НадписьДатаВремяНажатие (Элемент) {
    var КонструкторФорматнойСтроки = v8New("КонструкторФорматнойСтроки");
    КонструкторФорматнойСтроки.ДоступныеТипы = v8New("ОписаниеТипов", "Дата");
    if (КонструкторФорматнойСтроки.ОткрытьМодально())
        addToSignatureFormat(form, "ДатаВремя#" + КонструкторФорматнойСтроки.Текст);
}
//} Обработчики элементов управления формы

//{ Горячие клавиши по умолчанию.
function getPredefinedHotkeys(predef) {
    predef.setVersion(1);
    predef.add('Маркер "Добавлено"', "Alt + A");
    predef.add('Маркер "Изменено"', "Alt + C");
    predef.add('Маркер "Удалено"', "Alt + D");
}
//} Горячие клавиши по умолчанию.

//{ Параметры подстановки, используемые в форматной строке подписи.
addFormatStringParam("ИмяПользователя", "parseTpl(name)")
addFormatStringParam("ПолноеИмяПользователя", "parseTpl(name)")
addFormatStringParam("ИмяПользователяХранилищаКонфигурации", "parseTpl(name)")
addFormatStringParam("ДатаВремя", "parseTpl(name, '\"' + p + '\"')")
addFormatStringParam('ИмяПользователяОС', "(new ActiveXObject('WScript.Shell')).ExpandEnvironmentStrings('%USERNAME%')");
//} Параметры подстановки, используемые в форматной строке подписи.

var Settings = getSettings();


