1
0
mirror of https://github.com/salexdv/bsl_console.git synced 2025-11-06 08:59:16 +02:00

Описание языка bsl в отдельном файле.

Провайдер (provideFoldingRanges) для определения сворачиваемых блоков (циклы, условия, тексты запросов).
Провайдер (provideHover) для простых всплывающих подсказок по глобальным функциям и классам.
В bslGlobals для keywords добавлено новое поле prefix, для автодополнения, например точкой с запятой.
This commit is contained in:
salexdv
2020-07-21 21:56:39 +03:00
parent 0d81829471
commit ce88c9d2fb
6 changed files with 476 additions and 237 deletions

View File

@@ -1,16 +1,16 @@
## 0.1.2 (20.07.2020) ## 0.1.2 (21.07.2020)
### Новое: ### Новое:
* Добавлена обработка подсказок для предопределенных значений элементов * Добавлена обработка подсказок для предопределенных значений элементов
* Сворачивание циклов, условий и текстов запросов
* Простые всплювающие подсказки для глобальных функций, перечислений и классов
### Улучшения: ### Улучшения:
* Отказ от хранения описаний конструкций языка и метаданных в файлах JSON из-за CORS policy * Отказ от хранения описаний конструкций языка и метаданных в файлах JSON из-за CORS policy
* Добавление **;** после ключевых слов *КонецЦикла*, *КонецЕсли* (поле Prefix в bslGlobals)
### Исправления: ### Исправления:
* Выделение ключевого слова *Выполнить* * Выделение ключевого слова *Выполнить*
### Исправления:
* Исправлен сниппет для выборки из регистра накопления
## 0.1.1 (19.07.2020) ## 0.1.1 (19.07.2020)
### Новое: ### Новое:

View File

@@ -5656,11 +5656,15 @@ define([], function () {
"Если": {}, "Если": {},
"Тогда": {}, "Тогда": {},
"ИначеЕсли": {}, "ИначеЕсли": {},
"КонецЦикла": {}, "КонецЦикла": {
"postfix": ";\n"
},
"Иначе": {}, "Иначе": {},
"Исключение": {}, "Исключение": {},
"КонецПопытки": {}, "КонецПопытки": {},
"КонецЕсли": {}, "КонецЕсли": {
"postfix": ";\n"
},
"Попытка": {}, "Попытка": {},
"Пока": {}, "Пока": {},
"Для": {}, "Для": {},
@@ -5693,7 +5697,9 @@ define([], function () {
"Else": {}, "Else": {},
"ElsIf": {}, "ElsIf": {},
"Then": {}, "Then": {},
"EndIf": {}, "EndIf": {
"postfix": ";\n"
},
"Try": {}, "Try": {},
"Except": {}, "Except": {},
"EndTry": {}, "EndTry": {},
@@ -5704,7 +5710,9 @@ define([], function () {
"In": {}, "In": {},
"To": {}, "To": {},
"Do": {}, "Do": {},
"EndDo": {}, "EndDo": {
"postfix": ";\n"
},
"NOT": {}, "NOT": {},
"AND": {}, "AND": {},
"OR": {}, "OR": {},

View File

@@ -21,7 +21,8 @@ class bslHelper {
constructor(model, position) { constructor(model, position) {
this.model = model; this.model = model;
this.position = position; this.lineNumber = position.lineNumber;
this.column = position.column;
let wordData = model.getWordAtPosition(position); let wordData = model.getWordAtPosition(position);
this.word = wordData ? wordData.word.toLowerCase() : ''; this.word = wordData ? wordData.word.toLowerCase() : '';
@@ -31,7 +32,7 @@ class bslHelper {
this.textBeforePosition = this.getTextBeforePosition(); this.textBeforePosition = this.getTextBeforePosition();
this.lastExpression = this.getLastExpression(); this.lastExpression = this.getLastExpression();
this.lastRawExpression = this.getLastRawExpression(); this.lastRawExpression = this.getLastRawExpression();
} }
@@ -83,7 +84,7 @@ class bslHelper {
*/ */
getFullTextBeforePosition() { getFullTextBeforePosition() {
return this.model.getValueInRange({ startLineNumber: 1, startColumn: 1, endLineNumber: this.position.lineNumber, endColumn: this.position.column }).trim().toLowerCase(); return this.model.getValueInRange({ startLineNumber: 1, startColumn: 1, endLineNumber: this.lineNumber, endColumn: this.column }).trim().toLowerCase();
} }
@@ -94,7 +95,7 @@ class bslHelper {
*/ */
getTextBeforePosition() { getTextBeforePosition() {
let text = this.model.getValueInRange({ startLineNumber: this.position.lineNumber, startColumn: 1, endLineNumber: this.position.lineNumber, endColumn: this.position.column }); let text = this.model.getValueInRange({ startLineNumber: this.lineNumber, startColumn: 1, endLineNumber: this.lineNumber, endColumn: this.column });
this.hasWhitespace = (text.substr(-1) == ' '); this.hasWhitespace = (text.substr(-1) == ' ');
return text.trim().toLowerCase(); return text.trim().toLowerCase();
@@ -271,7 +272,10 @@ class bslHelper {
else { else {
for (const [inkey, invalue] of Object.entries(value)) { for (const [inkey, invalue] of Object.entries(value)) {
values.push({ name: inkey, detail: '', description: '', postfix: '' }); let postfix = '';
if (invalue.hasOwnProperty('postfix'))
postfix = invalue.postfix;
values.push({ name: inkey, detail: '', description: '', postfix: postfix });
} }
} }
@@ -699,6 +703,48 @@ class bslHelper {
} }
/**
* Completition provider
*
* @returns {array} array of completition
*/
getCompletition() {
let suggestions = [];
if (!this.getClassCompletition(suggestions, bslGlobals.classes)) {
if (!this.getClassCompletition(suggestions, bslGlobals.systemEnum)) {
if (!this.getMetadataCompletition(suggestions, bslMetadata)) {
this.getCommonCompletition(suggestions, bslGlobals.keywords, monaco.languages.CompletionItemKind.Keyword.ru, true);
this.getCommonCompletition(suggestions, bslGlobals.keywords, monaco.languages.CompletionItemKind.Keyword.en, true);
if (this.requireClass()) {
this.getCommonCompletition(suggestions, bslGlobals.classes, monaco.languages.CompletionItemKind.Constructor, false);
}
else {
this.getCommonCompletition(suggestions, bslGlobals.globalfunctions, monaco.languages.CompletionItemKind.Function, true);
this.getCommonCompletition(suggestions, bslGlobals.globalvariables, monaco.languages.CompletionItemKind.Class, false);
this.getCommonCompletition(suggestions, bslGlobals.systemEnum, monaco.languages.CompletionItemKind.Enum, false);
}
this.getSnippets(suggestions, snippets);
}
}
}
if (suggestions.length)
return { suggestions: suggestions }
else
return [];
}
/** /**
* Returns array of parametrs as described in JSON-dictionary * Returns array of parametrs as described in JSON-dictionary
* for current node (method) * for current node (method)
@@ -1046,6 +1092,26 @@ class bslHelper {
} }
/**
* Signature help provider
*
* @returns {object} helper
*/
getSigHelp() {
let helper = this.getMetadataSigHelp(bslMetadata);
if (!helper)
helper = this.getClassSigHelp(bslGlobals.classes);
if (!helper)
helper = this.getCommonSigHelp(bslGlobals.globalfunctions);
if (helper)
return new SignatureHelpResult(helper);
}
/** /**
* Updates bslMetadata from JSON-string which * Updates bslMetadata from JSON-string which
* was received from 1C * was received from 1C
@@ -1076,4 +1142,183 @@ class bslHelper {
} }
/**
* Finds blocks like conditions (if...endif) and loops (while...enddo)
* when start column startString equal start column endString
*
* @param {ITextModel} current model of editor
* @param {string} regexp to detect opening construction
* @param {string} regexp to detect closing construction
*
* @returns {array} - array of folding ranges
*/
static getRangesForConstruction(model, startString, endString) {
let ranges = [];
const startMatches = model.findMatches("(?:^|\\b)?(" + startString + ") ", false, true)
let startMatch = null;
const endMatches = model.findMatches("(?:^|\\b)?(" + endString + ") ?;", false, true)
let endMatch = null;
let structFound = false;
let subidx = 0;
if (startMatches && endMatches) {
for (let idx = 0; idx < startMatches.length; idx++) {
structFound = false;
startMatch = startMatches[idx];
subidx = 0;
while (!structFound && subidx < endMatches.length) {
endMatch = endMatches[subidx];
if (endMatch.range.startColumn == startMatch.range.startColumn && startMatch.range.startLineNumber < endMatch.range.startLineNumber) {
structFound = true;
ranges.push(
{
kind: monaco.languages.FoldingRangeKind.Region,
start: startMatch.range.startLineNumber,
end: endMatch.range.startLineNumber
}
)
}
subidx++;
}
}
}
return ranges;
}
/**
* Finds blocks like functions by regexp
*
* @param {ITextModel} current model of editor
* @param {string} regexp to detect block
*
* @returns {array} - array of folding ranges
*/
static getRangesForRegexp(model, regexp) {
let ranges = [];
let match = null;
const matches = model.findMatches(regexp, false, true, false, null, true)
if (matches) {
for (let idx = 0; idx < matches.length; idx++) {
match = matches[idx];
ranges.push(
{
kind: monaco.languages.FoldingRangeKind.Region,
start: match.range.startLineNumber,
end: match.range.endLineNumber
}
)
}
}
return ranges;
}
/**
* Provider for folding blocks
* @param {ITextModel} current model of editor
*
* @returns {array} - array of folding ranges
*/
static getFoldingRanges(model) {
let ranges = this.getRangesForRegexp(model, "\"(?:\\n|\\r|\\|)*(?:выбрать|select)(?:(?:.|\\n|\\r)*?)?\"");
ranges = ranges.concat(this.getRangesForRegexp(model, "(?:^|\\b)(?:функция|процедура).*\\((?:.|\\n|\\r)*?(?:конецпроцедуры|конецфункции)"));
ranges = ranges.concat(this.getRangesForRegexp(model, "(?:^|\\b)#.+(?:.|\\n|\\r)*?#.+$"));
ranges = ranges.concat(this.getRangesForConstruction(model, "пока|while", "конеццикла|enddo"));
ranges = ranges.concat(this.getRangesForConstruction(model, "для .*(?:по|из) .*|for .* (?:to|each) .*", "конеццикла|enddo"));
ranges = ranges.concat(this.getRangesForConstruction(model, "если|if", "конецесли|endif"));
return ranges;
}
/**
* Provider for hover popoup
*
* @returns {object} - hover object or null
*/
getHover() {
for (const [key, value] of Object.entries(bslGlobals)) {
for (const [ikey, ivalue] of Object.entries(value)) {
if (ivalue.hasOwnProperty('name')) {
if (ivalue.name.toLowerCase() == this.word) {
let contents = [
{ value: '**' + ivalue.name + '**' },
{ value: ivalue.description }
]
if (ivalue.hasOwnProperty('returns')) {
contents.push(
{ value: 'Возвращает: ' + ivalue.returns }
)
}
return {
range: new monaco.Range(this.lineNumber, this.column, this.lineNumber, this.model.getLineMaxColumn(this.lineNumber)),
contents: contents
};
}
}
}
}
return null;
}
/**
* Returns query's text from current position
*
* @returns {object} object with text and range or null
*/
getQuery() {
const matches = this.model.findMatches("\"(?:\\n|\\r|\\|)*(?:выбрать|select)(?:(?:.|\\n|\\r)*?)?\"", false, true, false, null, true)
let idx = 0;
let match = null;
let queryFound = false;
if (matches) {
while (idx < matches.length && !queryFound) {
match = matches[idx];
queryFound = (match.range.startLineNumber <= this.lineNumber && this.lineNumber <= match.range.endLineNumber);
idx++;
}
}
return queryFound ? { text: match.matches[0], range: match.range } : null;
}
} }

165
src/bsl_language.js Normal file
View File

@@ -0,0 +1,165 @@
define([], function () {
language = {
id: 'bsl',
rules: {
defaultToken: '',
tokenPostfix: 'bsl',
ignoreCase: true,
brackets: [
{ open: '[', close: ']', token: 'delimiter.square' },
{ open: '(', close: ')', token: 'delimiter.parenthesis' },
],
keywords: [
'КонецПроцедуры', 'EndProcedure', 'КонецФункции', 'EndFunction',
'Прервать', 'Break', 'Продолжить', 'Continue', 'Возврат', 'Return',
'Если', 'If', 'Иначе', 'Else', 'ИначеЕсли', 'ElsIf', 'Тогда', 'Then',
'КонецЕсли', 'EndIf', 'Попытка', 'Try', 'Исключение', 'Except',
'КонецПопытки', 'EndTry', 'Raise', 'ВызватьИсключение', 'Пока',
'While', 'Для', 'For', 'Каждого', 'Each', 'Из', 'In', 'По', 'To', 'Цикл',
'Do', 'КонецЦикла', 'EndDo', 'НЕ', 'NOT', 'И', 'AND', 'ИЛИ', 'OR', 'Новый',
'New', 'Процедура', 'Procedure', 'Функция', 'Function', 'Перем', 'Var',
'Экспорт', 'Export', 'Знач', 'Val', 'Неопределено', 'Выполнить'
],
namespaceFollows: [
'namespace', 'using',
],
parenFollows: [
'if', 'for', 'while', 'switch', 'foreach', 'using', 'catch', 'when'
],
operators: ['=', '<=', '>=', '<>', '<', '>', '+', '-', '*', '/', '%'],
symbols: /[=><!~?:&+\-*\/\^%]+/,
// escape sequences
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
// The main tokenizer for our languages
tokenizer: {
root: [
[/[a-zA-Z\u0410-\u044F_][a-zA-Z\u0410-\u044F_0-9]*/, { cases: { '@keywords': 'keyword', '@default': 'identifier' } }],
// whitespace
{ include: '@whitespace' },
// delimiters and operators
[/}/, {
cases: {
'$S2==interpolatedstring': { token: 'string.quote', next: '@pop' },
'$S2==litinterpstring': { token: 'string.quote', next: '@pop' },
'@default': '@brackets'
}
}],
[/^\s*#.*$/, 'preproc'],
[/[()\[\]]/, '@brackets'],
[/@symbols/, {
cases: {
'@operators': 'delimiter',
'@default': ''
}
}],
// numbers
[/[0-9_]*\.[0-9_]+([eE][\-+]?\d+)?[fFdD]?/, 'number.float'],
[/[0-9_]+/, 'number'],
// delimiter: after number because of .\d floats
[/[;,.]/, 'delimiter'],
// strings
[/"([^"\\]|\\.)*$/, 'string.invalid'],
[/["|]/, { token: 'string.quote', next: '@string' }],
[/\$\@"/, { token: 'string.quote', next: '@litinterpstring' }],
[/\@"/, { token: 'string.quote', next: '@litstring' }],
[/\$"/, { token: 'string.quote', next: '@interpolatedstring' }],
// characters
[/'[^\\']'/, 'string'],
[/(')(@escapes)(')/, ['string', 'string.escape', 'string']],
[/'/, 'string.invalid']
],
comment: [
[/\/\/.*$/, 'comment'],
],
string: [
[/[^\\"]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/"/, { token: 'string.quote', next: '@pop' }],
[/\|.*"/, { token: 'string.quote', next: '@pop' }],
],
litstring: [
[/[^"]+/, 'string'],
[/""/, 'string.escape'],
[/"/, { token: 'string.quote', next: '@pop' }]
],
litinterpstring: [
[/[^"{]+/, 'string'],
[/""/, 'string.escape'],
[/{{/, 'string.escape'],
[/}}/, 'string.escape'],
[/{/, { token: 'string.quote', next: 'root.litinterpstring' }],
[/"/, { token: 'string.quote', next: '@pop' }]
],
interpolatedstring: [
[/[^\\"{]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/{{/, 'string.escape'],
[/}}/, 'string.escape'],
[/{/, { token: 'string.quote', next: 'root.interpolatedstring' }],
[/"/, { token: 'string.quote', next: '@pop' }]
],
whitespace: [
[/\/\/.*$/, 'comment'],
],
},
},
themes: {
whiteTheme: {
base: 'vs',
name: 'bsl-white',
inherit: true,
rules: [
{ token: 'commentbsl', foreground: '008000' },
{ token: 'keywordbsl', foreground: 'ff0000' },
{ token: 'delimiterbsl', foreground: 'ff0000' },
{ token: 'delimiter.squarebsl', foreground: 'ff0000' },
{ token: 'delimiter.parenthesisbsl', foreground: 'ff0000' },
{ token: 'identifierbsl', foreground: '0000ff' },
{ token: 'stringbsl', foreground: '000000' },
{ token: 'string.quotebsl', foreground: '000000' },
{ token: 'string.invalidbsl', foreground: '000000' },
{ token: 'numberbsl', foreground: '000000' },
{ token: 'number.floatbsl', foreground: '000000' },
{ token: 'preprocbsl', foreground: '963200' },
]
},
blackTheme: {
base: 'vs',
name: 'bsl-dark',
inherit: true,
colors: {
'foreground': '#d4d4d4',
'editor.background': '#1e1e1e',
'editor.selectionBackground': '#062f4a',
'editorCursor.foreground': '#d4d4d4',
'editorSuggestWidget.background': '#252526',
'editorSuggestWidget.foreground': '#d4d4d4',
'editorSuggestWidget.selectedBackground': '#062f4a',
'editorWidget.background': '#252526',
'editorWidget.foreground': '#d4d4d4',
'editorWidget.border': '#d4d4d4'
},
rules: [
{ token: 'commentbsl', foreground: '6A9955' },
{ token: 'keywordbsl', foreground: '499caa' },
{ token: 'delimiterbsl', foreground: 'd4d4d4' },
{ token: 'delimiter.squarebsl', foreground: 'd4d4d4' },
{ token: 'delimiter.parenthesisbsl', foreground: 'd4d4d4' },
{ token: 'identifierbsl', foreground: 'd4d4d4' },
{ token: 'stringbsl', foreground: 'c3602c' },
{ token: 'string.quotebsl', foreground: 'c3602c' },
{ token: 'string.invalidbsl', foreground: 'c3602c' },
{ token: 'numberbsl', foreground: 'b5cea8' },
{ token: 'number.floatbsl', foreground: 'b5cea8' },
{ token: 'preprocbsl', foreground: '963200' },
{ background: '#1e1e1e' }
]
}
}
}
});

View File

@@ -1,4 +1,4 @@
define(['bslGlobals', 'bslMetadata', 'snippets', 'vs/editor/editor.main'], function () { define(['bslGlobals', 'bslMetadata', 'snippets', 'bsl_language', 'vs/editor/editor.main'], function () {
setText = function(txt, range) { setText = function(txt, range) {
@@ -23,26 +23,8 @@ define(['bslGlobals', 'bslMetadata', 'snippets', 'vs/editor/editor.main'], funct
getQuery = function () { getQuery = function () {
let position = editor.getPosition(); let bsl = new bslHelper(editor.getModel(), editor.getPosition());
return bsl.getQuery();
const matches = editor.getModel().findMatches("\"(?:\\n|\\r|\\|)*(?:выбрать|select)(?:(?:.|\\n|\\r)*?)?\"", false, true, false, null, true)
const lineNumber = position.lineNumber;
let idx = 0;
let match = null;
let queryFound = false;
if (matches) {
while (idx < matches.length && !queryFound) {
match = matches[idx];
queryFound = (match.range.startLineNumber <= lineNumber && lineNumber <= match.range.endLineNumber);
idx++;
}
}
return queryFound ? { text: match.matches[0], range: match.range } : null;
} }
@@ -52,239 +34,68 @@ define(['bslGlobals', 'bslMetadata', 'snippets', 'vs/editor/editor.main'], funct
} }
setTheme = function (theme) {
monaco.editor.setTheme(theme);
}
// Register a new language // Register a new language
monaco.languages.register({ id: 'bsl' }); monaco.languages.register({ id: language.id });
// Register a tokens provider for the language // Register a tokens provider for the language
monaco.languages.setMonarchTokensProvider('bsl', { monaco.languages.setMonarchTokensProvider(language.id, language.rules);
defaultToken: '',
tokenPostfix: 'bsl',
brackets: [
{ open: '[', close: ']', token: 'delimiter.square' },
{ open: '(', close: ')', token: 'delimiter.parenthesis' },
],
keywords: [
'КонецПроцедуры', 'EndProcedure', 'КонецФункции', 'EndFunction',
'Прервать', 'Break', 'Продолжить', 'Continue', 'Возврат', 'Return',
'Если', 'If', 'Иначе', 'Else', 'ИначеЕсли', 'ElsIf', 'Тогда', 'Then',
'КонецЕсли', 'EndIf', 'Попытка', 'Try', 'Исключение', 'Except',
'КонецПопытки', 'EndTry', 'Raise', 'ВызватьИсключение', 'Пока',
'While', 'Для', 'For', 'Каждого', 'Each', 'Из', 'In', 'По', 'To', 'Цикл',
'Do', 'КонецЦикла', 'EndDo', 'НЕ', 'NOT', 'И', 'AND', 'ИЛИ', 'OR', 'Новый',
'New', 'Процедура', 'Procedure', 'Функция', 'Function', 'Перем', 'Var',
'Экспорт', 'Export', 'Знач', 'Val', 'Неопределено', 'Выполнить'
],
namespaceFollows: [
'namespace', 'using',
],
parenFollows: [
'if', 'for', 'while', 'switch', 'foreach', 'using', 'catch', 'when'
],
operators: ['=', '<=', '>=', '<>', '<', '>', '+', '-', '*', '/', '%'],
symbols: /[=><!~?:&+\-*\/\^%]+/,
// escape sequences
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
// The main tokenizer for our languages
tokenizer: {
root: [
[/[a-zA-Z\u0410-\u044F_][a-zA-Z\u0410-\u044F_0-9]*/, { cases: { '@keywords': 'keyword', '@default': 'identifier' } }],
// whitespace
{ include: '@whitespace' },
// delimiters and operators
[/}/, {
cases: {
'$S2==interpolatedstring': { token: 'string.quote', next: '@pop' },
'$S2==litinterpstring': { token: 'string.quote', next: '@pop' },
'@default': '@brackets'
}
}],
[/^\s*#.*$/, 'preproc'],
[/[()\[\]]/, '@brackets'],
[/@symbols/, {
cases: {
'@operators': 'delimiter',
'@default': ''
}
}],
// numbers
[/[0-9_]*\.[0-9_]+([eE][\-+]?\d+)?[fFdD]?/, 'number.float'],
[/[0-9_]+/, 'number'],
// delimiter: after number because of .\d floats
[/[;,.]/, 'delimiter'],
// strings
[/"([^"\\]|\\.)*$/, 'string.invalid'],
[/["|]/, { token: 'string.quote', next: '@string' }],
[/\$\@"/, { token: 'string.quote', next: '@litinterpstring' }],
[/\@"/, { token: 'string.quote', next: '@litstring' }],
[/\$"/, { token: 'string.quote', next: '@interpolatedstring' }],
// characters
[/'[^\\']'/, 'string'],
[/(')(@escapes)(')/, ['string', 'string.escape', 'string']],
[/'/, 'string.invalid']
],
comment: [
[/\/\/.*$/, 'comment'],
],
string: [
[/[^\\"]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/"/, { token: 'string.quote', next: '@pop' }],
[/\|.*"/, { token: 'string.quote', next: '@pop' }],
],
litstring: [
[/[^"]+/, 'string'],
[/""/, 'string.escape'],
[/"/, { token: 'string.quote', next: '@pop' }]
],
litinterpstring: [
[/[^"{]+/, 'string'],
[/""/, 'string.escape'],
[/{{/, 'string.escape'],
[/}}/, 'string.escape'],
[/{/, { token: 'string.quote', next: 'root.litinterpstring' }],
[/"/, { token: 'string.quote', next: '@pop' }]
],
interpolatedstring: [
[/[^\\"{]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/{{/, 'string.escape'],
[/}}/, 'string.escape'],
[/{/, { token: 'string.quote', next: 'root.interpolatedstring' }],
[/"/, { token: 'string.quote', next: '@pop' }]
],
whitespace: [
[/\/\/.*$/, 'comment'],
],
},
});
// Register a completion item provider for the new language // Register a completion item provider for the new language
monaco.languages.registerCompletionItemProvider('bsl', { monaco.languages.registerCompletionItemProvider(language.id, {
triggerCharacters: [' ', '.'], triggerCharacters: [' ', '.'],
provideCompletionItems: function (model, position) { provideCompletionItems: function (model, position) {
let suggestions = [];
let bsl = new bslHelper(model, position); let bsl = new bslHelper(model, position);
return bsl.getCompletition();
if (!bsl.getClassCompletition(suggestions, bslGlobals.classes)) {
if (!bsl.getClassCompletition(suggestions, bslGlobals.systemEnum)) {
if (!bsl.getMetadataCompletition(suggestions, bslMetadata)) {
bsl.getCommonCompletition(suggestions, bslGlobals.keywords, monaco.languages.CompletionItemKind.Keyword.ru, true);
bsl.getCommonCompletition(suggestions, bslGlobals.keywords, monaco.languages.CompletionItemKind.Keyword.en, true);
if (bsl.requireClass()) {
bsl.getCommonCompletition(suggestions, bslGlobals.classes, monaco.languages.CompletionItemKind.Constructor, false);
}
else {
bsl.getCommonCompletition(suggestions, bslGlobals.globalfunctions, monaco.languages.CompletionItemKind.Function, true);
bsl.getCommonCompletition(suggestions, bslGlobals.globalvariables, monaco.languages.CompletionItemKind.Class, false);
bsl.getCommonCompletition(suggestions, bslGlobals.systemEnum, monaco.languages.CompletionItemKind.Enum, false);
}
bsl.getSnippets(suggestions, snippets);
}
}
}
if (suggestions.length)
return { suggestions: suggestions }
else
return [];
} }
}); });
monaco.languages.registerSignatureHelpProvider('bsl', { monaco.languages.registerFoldingRangeProvider(language.id, {
provideFoldingRanges: function (model, context, token) {
return bslHelper.getFoldingRanges(model);
}
});
monaco.languages.registerSignatureHelpProvider(language.id, {
signatureHelpTriggerCharacters: ['(', ','], signatureHelpTriggerCharacters: ['(', ','],
provideSignatureHelp: (model, position) => { provideSignatureHelp: (model, position) => {
let bsl = new bslHelper(model, position); let bsl = new bslHelper(model, position);
let helper = bsl.getMetadataSigHelp(bslMetadata); return bsl.getSigHelp();
if (!helper)
helper = bsl.getClassSigHelp(bslGlobals.classes);
if (!helper)
helper = bsl.getCommonSigHelp(bslGlobals.globalfunctions);
if (helper)
return new SignatureHelpResult(helper);
} }
}); });
monaco.editor.defineTheme('bsl-white', { monaco.languages.registerHoverProvider(language.id, {
base: 'vs',
inherit: true,
rules: [
{ token: 'commentbsl', foreground: '008000' },
{ token: 'keywordbsl', foreground: 'ff0000' },
{ token: 'delimiterbsl', foreground: 'ff0000' },
{ token: 'delimiter.squarebsl', foreground: 'ff0000' },
{ token: 'delimiter.parenthesisbsl', foreground: 'ff0000' },
{ token: 'identifierbsl', foreground: '0000ff' },
{ token: 'stringbsl', foreground: '000000' },
{ token: 'string.quotebsl', foreground: '000000' },
{ token: 'string.invalidbsl', foreground: '000000' },
{ token: 'numberbsl', foreground: '000000' },
{ token: 'number.floatbsl', foreground: '000000' },
{ token: 'preprocbsl', foreground: '963200' },
]
});
monaco.editor.defineTheme('bsl-dark', { provideHover: function (model, position) {
base: 'vs', let bsl = new bslHelper(model, position);
inherit: true, return bsl.getHover();
colors: { }
'foreground': '#d4d4d4',
'editor.background': '#1e1e1e',
'editor.selectionBackground': '#062f4a',
'editorCursor.foreground': '#d4d4d4',
'editorSuggestWidget.background': '#252526',
'editorSuggestWidget.foreground': '#d4d4d4',
'editorSuggestWidget.selectedBackground': '#062f4a',
'editorWidget.background': '#252526',
'editorWidget.foreground': '#d4d4d4',
'editorWidget.border': '#d4d4d4'
},
rules: [
{ token: 'commentbsl', foreground: '6A9955' },
{ token: 'keywordbsl', foreground: '499caa' },
{ token: 'delimiterbsl', foreground: 'd4d4d4' },
{ token: 'delimiter.squarebsl', foreground: 'd4d4d4' },
{ token: 'delimiter.parenthesisbsl', foreground: 'd4d4d4' },
{ token: 'identifierbsl', foreground: 'd4d4d4' },
{ token: 'stringbsl', foreground: 'c3602c' },
{ token: 'string.quotebsl', foreground: 'c3602c' },
{ token: 'string.invalidbsl', foreground: 'c3602c' },
{ token: 'numberbsl', foreground: 'b5cea8' },
{ token: 'number.floatbsl', foreground: 'b5cea8' },
{ token: 'preprocbsl', foreground: '963200' },
{ background: '#1e1e1e' }
]
});
monaco.editor.setTheme('bsl-dark'); });
for (const [key, value] of Object.entries(language.themes)) {
monaco.editor.defineTheme(value.name, value);
monaco.editor.setTheme(value.name);
}
editor = monaco.editor.create(document.getElementById("container"), { editor = monaco.editor.create(document.getElementById("container"), {
theme: "bsl-white", theme: "bsl-white",
value: getCode(), value: getCode(),
language: 'bsl' language: language.id
}); });
}); });

View File

@@ -239,6 +239,16 @@ describe("Проверка автокомлита и подсказок реда
bslMetadata = JSON.parse(JSON.stringify(mCopy)); bslMetadata = JSON.parse(JSON.stringify(mCopy));
}); });
it("проверка всплывающей подсказки", function () {
let model = getModel("Найти(");
let position = new monaco.Position(1, 2);
bsl = new bslHelper(model, position);
assert.notEqual(bsl.getHover(), null);
model = getModel("НайтиЧтоНибудь(");
bsl = new bslHelper(model, position);
assert.equal(bsl.getHover(), null);
});
} }
mocha.run(); mocha.run();