mirror of
https://github.com/tormozit/RDT1C.git
synced 2025-12-19 06:04:13 +00:00
1007619 Bug Исправлен иногда некорректный расчет анализатором кода вызовов из расширения модуля в общие модули Действия История Гита 1 1007618 Bug Исправлены недочеты в отборе коммитов при полю "Что искать" Действия Консоль запросов 1 1007609 Bug Восстановлено запоминание сверток панелей при закрытии формы Действия Конструктор запроса 1 1007605 Task Команда "Заполнить настройки компоновки" разделена в подменю из двух команд "Очистить настройки" и "Заполнить настройки" Действия Поле текста программы 3 1007598 Task Теперь автодополнение в языке запросов предлагает имена новых параметров после "&" в правой части сравнения Действия 1007610 Bug Восстановлено заполнение списка автодополнения встроенными функциями и функциями модулей языка выражений компоновки данных Действия 1007603 Bug Устранен захват в структуре модуля первым методом инструкции препроцессора с предшествующим комментарием Действия Различные значения колонки 1 1007611 Bug Исправлена ошибка открытия из динамического списка в клиент-серверной базе Действия Чат ИИ 2 1007620 Bug Исправлены недочеты раскраски блоков кода 1С в ответах ИИ Действия 1007616 Bug Улучшена обработка ошибок соединения с Напарником
405 lines
15 KiB
Plaintext
405 lines
15 KiB
Plaintext
<!-- для выполнения в Webkit 605 внутри поля HTML формы платформы 1С -->
|
||
<html>
|
||
<head>
|
||
<script type="text/javascript">
|
||
%ТекстJS%
|
||
</script>
|
||
<style type=text/css>
|
||
body {
|
||
margin: 2px;
|
||
}
|
||
pre {
|
||
font-family: Courier;
|
||
color: #0000FF;
|
||
font-size: 9pt;
|
||
}
|
||
.k { color: red; }
|
||
.c { color: green; }
|
||
.s { color: black; }
|
||
.n { color: black; }
|
||
.p { color: brown; }
|
||
TABLE {
|
||
BORDER-TOP: black 1px solid;
|
||
BORDER-RIGHT: black 1px solid;
|
||
BORDER-COLLAPSE: collapse;
|
||
BORDER-BOTTOM: black 1px solid;
|
||
BORDER-LEFT: black 1px solid;
|
||
border-color: #C2C2C2;
|
||
}
|
||
TH {
|
||
BORDER-TOP: black 1px solid;
|
||
BORDER-RIGHT: black 1px solid;
|
||
BORDER-BOTTOM: black 1px solid;
|
||
PADDING-BOTTOM: 5px;
|
||
PADDING-TOP: 5px;
|
||
PADDING-LEFT: 5px;
|
||
BORDER-LEFT: black 1px solid;
|
||
PADDING-RIGHT: 5px;
|
||
border-color: #C2C2C2;
|
||
}
|
||
TD {
|
||
BORDER-TOP: black 1px solid;
|
||
BORDER-RIGHT: black 1px solid;
|
||
BORDER-BOTTOM: black 1px solid;
|
||
PADDING-BOTTOM: 5px;
|
||
PADDING-TOP: 5px;
|
||
PADDING-LEFT: 5px;
|
||
BORDER-LEFT: black 1px solid;
|
||
PADDING-RIGHT: 5px;
|
||
border-color: #C2C2C2;
|
||
}
|
||
blockquote {
|
||
background: #f9f9f9;
|
||
border-left: 5px solid #ccc;
|
||
margin: 1.5em 10px;
|
||
padding: 0.5em 10px;
|
||
}
|
||
blockquote:before {
|
||
color: #ccc;
|
||
content: open-quote;
|
||
font-size: 4em;
|
||
line-height: 0.1em;
|
||
margin-right: 0.25em;
|
||
vertical-align: -0.4em;
|
||
}
|
||
blockquote p {
|
||
display: inline;
|
||
}
|
||
/* Стили для блоков кода 1С */
|
||
.code-block-1c {
|
||
border: 1px solid #ccc;
|
||
margin: 10px 0;
|
||
background: #f0f0f0;
|
||
}
|
||
.code-header, .code-footer {
|
||
background: #F0F0F0;
|
||
border-bottom: 1px solid #ccc;
|
||
padding: 2px 4px; /* Уменьшена высота */
|
||
font-size: 11px; /* Уменьшен размер шрифта */
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.code-footer {
|
||
border-top: 1px solid #ccc;
|
||
border-bottom: none;
|
||
}
|
||
.code-header span, .code-footer span {
|
||
font-weight: bold;
|
||
color: #666;
|
||
}
|
||
.code-header button, .code-footer button {
|
||
background: #e0e0e0; /* Приглушённый фон */
|
||
border: 1px solid #999; /* Приглушённая рамка */
|
||
color: #666; /* Приглушённый цвет текста */
|
||
margin: 0 2px;
|
||
margin-right: auto; /* Прижимаем к левому краю */
|
||
padding: 1px 6px; /* Уменьшено внутреннее пространство */
|
||
font-size: 10px; /* Уменьшен размер шрифта кнопки */
|
||
cursor: pointer;
|
||
}
|
||
.code-header button:hover, .code-footer button:hover {
|
||
background: #d0d0d0; /* Приглушённый фон при наведении */
|
||
}
|
||
.code-content {
|
||
padding: 8px;
|
||
margin: 0;
|
||
overflow: auto;
|
||
background: white;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 13px;
|
||
line-height: 1.2;
|
||
max-height: 400px;
|
||
}
|
||
/* Цвета синтаксиса 1С - стандартные цвета */
|
||
.c1c-keyword { color: #FF0000; } /* Красный для ключевых слов */
|
||
.c1c-symbol { color: #FF0000; } /* Красный для спецсимволов */
|
||
.c1c-comment { color: #008000; } /* Зеленый для комментариев */
|
||
.c1c-string { color: #000000; } /* Черный для строк */
|
||
.c1c-number { color: #000000; } /* Черный для чисел */
|
||
.c1c-constant { color: #0000FF; } /* Синий для констант */
|
||
.c1c-identifier { color: #0000FF; } /* Синий для идентификаторов */
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="content"></div>
|
||
<script>
|
||
function highlight1C(code) {
|
||
if (!code) return '';
|
||
code = code.replace(new RegExp('\\t', 'g'), ' ');
|
||
|
||
var keywords = [
|
||
'Процедура', 'Функция', 'КонецПроцедуры', 'КонецФункции',
|
||
'Если', 'Тогда', 'Иначе', 'КонецЕсли', 'Для', 'Каждого',
|
||
'Из', 'Цикл', 'КонецЦикла', 'Пока', 'Выполнять', 'Возврат',
|
||
'Продолжить', 'Прервать', 'Попытка', 'Исключение', 'КонецПопытки',
|
||
'ВызватьИсключение', 'Перем', 'Перейти', 'Не', 'И',
|
||
'Или', 'ИначеЕсли', 'Экспорт', 'Знач', 'Новый', 'Истина', "Ложь"
|
||
];
|
||
var constants = ['Истина', 'Ложь', 'Неопределено', 'Null'];
|
||
var tokens = [];
|
||
var tokenIndex = 0;
|
||
|
||
function createToken(content) {
|
||
var marker = '___TOKEN_' + tokenIndex + '___';
|
||
tokens.push({ marker: marker, content: content });
|
||
tokenIndex++;
|
||
return marker;
|
||
}
|
||
|
||
// Шаг 1: Обрабатываем строки ДО любого экранирования
|
||
// В строке пишем четыре слеша, чтобы в регулярку попало два (для экранирования точки)
|
||
var stringRegex = new RegExp('"([^"\\\\]|\\\\.)*"', 'g');
|
||
code = code.replace(stringRegex, function(match) {
|
||
return createToken('___STRING___' + match + '___STRING___');
|
||
});
|
||
|
||
// Шаг 2: Обрабатываем комментарии 1С (// ...)
|
||
code = code.replace(new RegExp('\\/\\/.*$', 'gm'), function(match) {
|
||
return createToken('<span class="c1c-comment">' + match + '</span>');
|
||
});
|
||
|
||
// Шаг 3: Обрабатываем директивы препроцессора &...
|
||
var directiveRegex = new RegExp('&[А-Яа-яЁёA-Za-z_][А-Яа-яЁёA-Za-z_0-9]*', 'g');
|
||
code = code.replace(directiveRegex, function(match) {
|
||
return createToken('<span class="c1c-keyword">' + match + '</span>');
|
||
});
|
||
|
||
// Шаг 3.5: Обрабатываем операторы сравнения < и > отдельно
|
||
code = code.replace(new RegExp('([<>])', 'g'), function(match) {
|
||
var safeChar = (match === '<') ? '<' : '>';
|
||
return createToken('<span class="c1c-symbol">' + safeChar + '</span>');
|
||
});
|
||
|
||
// Шаг 4: Экранируем HTML-опасные символы
|
||
var rgxStringMarker = new RegExp('___STRING___', 'g');
|
||
var rgxAmp = new RegExp('&', 'g');
|
||
var rgxLt = new RegExp('<', 'g');
|
||
var rgxGt = new RegExp('>', 'g');
|
||
|
||
for (var i = 0; i < tokens.length; i++) {
|
||
if (tokens[i].content.indexOf('___STRING___') !== -1) {
|
||
var stringContent = tokens[i].content.replace(rgxStringMarker, '');
|
||
var escapedString = stringContent
|
||
.replace(rgxAmp, '&')
|
||
.replace(rgxLt, '<')
|
||
.replace(rgxGt, '>');
|
||
tokens[i].content = '<span class="c1c-string">' + escapedString + '</span>';
|
||
}
|
||
}
|
||
|
||
// Экранируем оставшийся код
|
||
code = code
|
||
.replace(rgxAmp, '&')
|
||
.replace(rgxLt, '<')
|
||
.replace(rgxGt, '>');
|
||
|
||
// Шаг 5: Обрабатываем числа
|
||
code = code.replace(new RegExp('\\b(\\d+\\.?\\d*)\\b', 'g'), function(match) {
|
||
return createToken('<span class="c1c-number">' + match + '</span>');
|
||
});
|
||
|
||
// Шаг 6: Обрабатываем ключевые слова с кастомными границами
|
||
var wordBoundaryPrefix = '([^а-яёa-z0-9_]|^)';
|
||
var wordBoundarySuffix = '(?=[^а-яёa-z0-9_]|$)';
|
||
|
||
for (var i = 0; i < keywords.length; i++) {
|
||
// Экранируем спецсимволы в самом ключевом слове
|
||
var escapedKeyword = keywords[i].replace(new RegExp('[.*+?^${}()|[\\]\\\\]', 'g'), '\\$&');
|
||
var regex = new RegExp(wordBoundaryPrefix + '(' + escapedKeyword + ')' + wordBoundarySuffix, 'gi');
|
||
|
||
code = code.replace(regex, function(match, prefix, kw) {
|
||
return prefix + createToken('<span class="c1c-keyword">' + kw + '</span>');
|
||
});
|
||
}
|
||
|
||
// Шаг 7: Обрабатываем константы
|
||
for (var j = 0; j < constants.length; j++) {
|
||
var escapedConstant = constants[j].replace(new RegExp('[.*+?^${}()|[\\]\\\\]', 'g'), '\\$&');
|
||
var constRegex = new RegExp(wordBoundaryPrefix + '(' + escapedConstant + ')' + wordBoundarySuffix, 'gi');
|
||
|
||
code = code.replace(constRegex, function(match, prefix, cnst) {
|
||
return prefix + createToken('<span class="c1c-constant">' + cnst + '</span>');
|
||
});
|
||
}
|
||
|
||
// Шаг 8: Обрабатываем идентификаторы
|
||
var identRegexStr = '([^а-яёa-z0-9_]|^)([а-яёa-z_][а-яёa-z0-9_]*)(?=[^а-яёa-z0-9_]|$)';
|
||
var identRegex = new RegExp(identRegexStr, 'gi');
|
||
|
||
code = code.replace(identRegex, function(match, prefix, word) {
|
||
if (word.indexOf('___TOKEN_') !== 0) {
|
||
return prefix + createToken('<span class="c1c-identifier">' + word + '</span>');
|
||
}
|
||
return match;
|
||
});
|
||
|
||
// Шаг 9: Обрабатываем спецсимволы
|
||
// Внимание: внутри [] нужно экранировать минус и скобки
|
||
var symbolRegex = new RegExp('([{}()\\[\\].,;:=+\\-*\\/])', 'g');
|
||
|
||
code = code.replace(symbolRegex, function(match) {
|
||
if (match.indexOf('___TOKEN_') !== 0) {
|
||
return createToken('<span class="c1c-symbol">' + match + '</span>');
|
||
}
|
||
return match;
|
||
});
|
||
|
||
// Шаг 10: Восстанавливаем все токены
|
||
for (var i = 0; i < tokens.length; i++) {
|
||
// Экранируем маркер для использования в RegExp
|
||
var escapedMarker = tokens[i].marker.replace(new RegExp('[.*+?^${}()|[\\]\\\\]', 'g'), '\\$&');
|
||
var regex = new RegExp(escapedMarker, 'g');
|
||
code = code.replace(regex, tokens[i].content);
|
||
}
|
||
|
||
// Шаг 11: Чистка
|
||
code = code.replace(new RegExp('___TOKEN_\\d+___', 'g'), '');
|
||
code = code.replace(new RegExp('___STRING___', 'g'), '');
|
||
|
||
return code;
|
||
}
|
||
|
||
// Функция копирования в буфер
|
||
function copyToClipboard(text) {
|
||
try {
|
||
var textArea = document.createElement('textarea');
|
||
textArea.value = text;
|
||
textArea.style.position = 'fixed';
|
||
textArea.style.left = '-999999px';
|
||
textArea.style.top = '-999999px';
|
||
document.body.appendChild(textArea);
|
||
textArea.focus();
|
||
textArea.select();
|
||
var successful = document.execCommand('copy');
|
||
document.body.removeChild(textArea);
|
||
return successful;
|
||
} catch (err) {
|
||
console.error('Не удалось скопировать текст: ', err);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Обработчик кнопок
|
||
function handleCodeAction(action, btn) {
|
||
var codeBlock = btn.closest('.code-block-1c');
|
||
if (!codeBlock) {
|
||
codeBlock = btn.parentNode.parentNode;
|
||
}
|
||
var codeContent = codeBlock.querySelector('.code-content');
|
||
if (!codeContent) {
|
||
console.error('Не найден элемент с кодом');
|
||
return;
|
||
}
|
||
// Получаем чистый текст, убирая HTML-теги
|
||
var text = codeContent.textContent || codeContent.innerText || '';
|
||
if (action === 'copy') {
|
||
var success = copyToClipboard(text);
|
||
if (success) {
|
||
// Визуальная обратная связь
|
||
var originalText = btn.textContent;
|
||
btn.textContent = 'Скопировано!';
|
||
btn.style.backgroundColor = '#90EE90';
|
||
setTimeout(function() {
|
||
btn.textContent = originalText;
|
||
btn.style.backgroundColor = '';
|
||
}, 1500);
|
||
} else {
|
||
btn.textContent = 'Ошибка!';
|
||
btn.style.backgroundColor = '#FFB6C1';
|
||
setTimeout(function() {
|
||
btn.textContent = 'Копировать';
|
||
btn.style.backgroundColor = '';
|
||
}, 1500);
|
||
}
|
||
}
|
||
// Передаем событие в 1С
|
||
if (window.handle1CCodeAction) {
|
||
window.handle1CCodeAction(action, text);
|
||
}
|
||
}
|
||
|
||
// Инициализация marked
|
||
if (typeof marked !== 'undefined') {
|
||
var originalRenderer = new marked.Renderer();
|
||
var originalCode = originalRenderer.code;
|
||
marked.Renderer.prototype.code = function(code, language) {
|
||
if (language && (false
|
||
|| language.toLowerCase() === '1с'
|
||
|| language.toLowerCase() === '1c'
|
||
|| language.toLowerCase() === 'bsl'))
|
||
{
|
||
var highlighted = highlight1C(code);
|
||
var lines = code.split('\n'); // Исправлено: использовать \n для подсчета строк
|
||
var hasFooter = lines.length > 20;
|
||
var html = '<div class="code-block-1c">' +
|
||
'<div class="code-header">' +
|
||
'<span>1C</span>' +
|
||
'<div>' +
|
||
'<button onclick="handleCodeAction(\'copy\', this)">Копировать</button>' +
|
||
'<button onclick="handleCodeAction(\'compare\', this)">Сравнить</button>' +
|
||
'</div>' +
|
||
'</div>' +
|
||
'<pre class="code-content">' + highlighted + '</pre>';
|
||
if (hasFooter) {
|
||
html += '<div class="code-footer">' +
|
||
'<span>1C</span>' +
|
||
'<div>' +
|
||
'<button onclick="handleCodeAction(\'copy\', this)">Копировать</button>' +
|
||
'<button onclick="handleCodeAction(\'compare\', this)">Сравнить</button>' +
|
||
'</div>' +
|
||
'</div>';
|
||
}
|
||
html += '</div>';
|
||
return html;
|
||
}
|
||
return originalCode.call(this, code, language);
|
||
};
|
||
marked.setOptions({
|
||
breaks: true,
|
||
gfm: true
|
||
});
|
||
}
|
||
|
||
window._1CAPI = window._1CAPI || {};
|
||
|
||
// Функция установки текста (работает с marked и без него)
|
||
window._1CAPI.setText = function(text, fontSize) {
|
||
var contentElement = document.getElementById('content');
|
||
|
||
// Устанавливаем размер шрифта для блоков кода 1С
|
||
if (fontSize) {
|
||
// Создаем или обновляем стиль для размера шрифта
|
||
var styleId = 'dynamic-code-font-size';
|
||
var existingStyle = document.getElementById(styleId);
|
||
|
||
if (existingStyle) {
|
||
existingStyle.remove();
|
||
}
|
||
|
||
var style = document.createElement('style');
|
||
style.id = styleId;
|
||
style.textContent = '.code-block-1c .code-content, #content pre { font-size: ' + fontSize + ' !important; }';
|
||
document.head.appendChild(style);
|
||
}
|
||
|
||
if (typeof marked !== 'undefined') {
|
||
// Используем marked если доступен
|
||
try {
|
||
contentElement.innerHTML = marked(text);
|
||
return true;
|
||
} catch (e) {
|
||
console.error('Ошибка marked:', e);
|
||
// Fallback на простой текст
|
||
contentElement.innerText = text;
|
||
return false;
|
||
}
|
||
} else {
|
||
// Режим без marked - просто выводим текст как есть
|
||
contentElement.innerHTML = '<pre>' + highlight1C(text) + '</pre>';
|
||
return true;
|
||
}
|
||
};
|
||
</script>
|
||
</body>
|
||
</html> |