Skip to content

Commit

Permalink
feat: вывод описания символов
Browse files Browse the repository at this point in the history
  • Loading branch information
alkoleft committed Jan 8, 2025
1 parent d5ae09a commit 128895c
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 27 deletions.
5 changes: 3 additions & 2 deletions src/bsl/features/completionItemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const completionItemProvider: languages.CompletionItemProvider = {

scope.forEachMembers(m => suggestions.push(newCompletionItem(m, range)))
console.debug('suggestions', suggestions)

return {
suggestions: suggestions
}
Expand All @@ -35,7 +35,8 @@ function newCompletionItem(symbol: Symbol, range: Range): languages.CompletionIt
label: symbol.name,
kind: completionItemKind(symbol.kind),
range: range,
insertText: insertText
insertText: insertText,
documentation: symbol.description
}
}

Expand Down
33 changes: 33 additions & 0 deletions src/bsl/features/signatureHelpProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { editor, languages, Position } from 'monaco-editor';
import { scopeProvider } from '../scopeProvider';

const signatureHelpProvider: languages.SignatureHelpProvider = {
signatureHelpRetriggerCharacters: ['(', ','],
signatureHelpTriggerCharacters: [')'],

provideSignatureHelp(model: editor.ITextModel, position: Position): languages.ProviderResult<languages.SignatureHelpResult> {
const symbol = scopeProvider.currentSymbol(model, position)
if (symbol) {
return {
value: {
signatures: [{
label: symbol.name,
parameters: [],
documentation: {
value: symbol.description??''
}
}],
activeParameter: 0,
activeSignature: 0
}, dispose: () => { }
}
} else {
return undefined
}

},
}

export {
signatureHelpProvider
}
7 changes: 5 additions & 2 deletions src/bsl/platform/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19603,14 +19603,17 @@ GlobalScope.registerGlobalSymbols(definition.map(d => {
const t = new PredefinedType(d.name_en, d.values.map(v => {
return {
name: v.name,
kind: SymbolType.property, type: 'unknown'
kind: SymbolType.property,
description: (v as any).description ?? '',
type: 'unknown'
}
}))
symbols.push(t)
return {
name: d.name,
kind: SymbolType.enum,
type: d.name_en
type: d.name_en,
description: d.description
}
}))

Expand Down
5 changes: 3 additions & 2 deletions src/bsl/platform/globalFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4993,7 +4993,7 @@ const definition = [
{
"name": "УстановитьОбновлениеПредопределенныхДанныхИнформационнойБазы",
"name_en": "SetInfoBasePredefinedDataUpdate",
"description": "Устанавливает новое значение режима обновления предопределенных данных для информационной базы. Для выполнения требуются права администратора информационной базы. - Если для объекта метаданных в данных установлен режим обновления, отличный от Авто, то используется это значение. - Иначе, если для объекта метаданных в конфигурации установлен режим обновления, отличный от Авто, то используется это значение. - Иначе, если для информационной базы установлен режим обновления, отличный от Авто, то используется это значение. - Иначе, если это периферийный узел РИБ, то предопределенные данные не будут обновлены. Если проверка выполняется для центрального узла РИБ, или для базы не являющейся РИБ, обновление предопределенных данных будет выполнено. Метод доступен только из сеанса, в котором не используется ни один разделитель.",
"description": "Устанавливает новое значение режима обновления предопределенных данных для информационной базы. Для выполнения требуются права администратора информационной базы.\n- Если для объекта метаданных в данных установлен режим обновления, отличный от Авто, то используется это значение.\n- Иначе, если для объекта метаданных в конфигурации установлен режим обновления, отличный от Авто, то используется это значение.\n- Иначе, если для информационной базы установлен режим обновления, отличный от Авто, то используется это значение.\n- Иначе, если это периферийный узел РИБ, то предопределенные данные не будут обновлены. Если проверка выполняется для центрального узла РИБ, или для базы не являющейся РИБ, обновление предопределенных данных будет выполнено. Метод доступен только из сеанса, в котором не используется ни один разделитель.",
"signature": {
"default": {
"СтрокаПараметров": "(ОбновлениеПредопределенныхДанных?: ОбновлениеПредопределенныхДанных)",
Expand Down Expand Up @@ -5292,6 +5292,7 @@ GlobalScope.registerGlobalSymbols(definition.map(d => {
return {
name: d.name,
kind: SymbolType.function,
type: d.returns
type: d.returns,
description: d.description
}
}))
3 changes: 2 additions & 1 deletion src/bsl/platform/globalVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ GlobalScope.registerGlobalSymbols(definition.map(d => {
return {
name: d.name,
kind: SymbolType.property,
type: d.name_en
type: d.name_en,
description: d.description
}
}))
63 changes: 49 additions & 14 deletions src/bsl/scopeProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { editor, Position } from 'monaco-editor';
import tokensProvider, { TokensSequence } from './tokensProvider'
import { getModelScope, UnionScope } from '../scope/scopeStore';
import { Scope } from '../scope/Scope';
import { Scope, Symbol } from '../scope/Scope';
import globalScope from '../scope/globalScope'

const scopeProvider = {
Expand All @@ -20,6 +20,29 @@ const scopeProvider = {
} else {
return objectScope(tokensSequence, scope, position.lineNumber)
}
},
currentSymbol(model: editor.ITextModel, position: Position): Symbol | undefined {
const tokensSequence = tokensProvider.resolve(model, position)

console.debug('tokensSequence: ', tokensSequence)
if (tokensSequence === undefined || tokensSequence.lastSymbol === ')') {
return undefined
}

const scope = getModelScope(model)
const word = model.getWordAtPosition(position)?.word
return currentMember(tokensSequence, scope, position.lineNumber, word)
}
}

function currentMember(tokensSequence: TokensSequence, unionScope: UnionScope, lineNumber: number, word?:string): Symbol | undefined {
tokensSequence.closed = false
if (tokensSequence.tokens.length === 1) {
return globalScopeMember(word??tokensSequence.tokens[0], unionScope, lineNumber)
}
const scope = objectScope(tokensSequence, unionScope, lineNumber)
if (scope) {
return findMember(scope, word??tokensSequence.tokens[tokensSequence.tokens.length - 1])
}
}

Expand All @@ -28,8 +51,8 @@ function objectScope(tokensSequence: TokensSequence, unionScope: UnionScope, lin
console.debug('calculate objectScope');

const tokens = tokensSequence.tokens
const lastToken = tokens[tokens.length - 1];
let scope = resolveInUnionScope(lastToken, unionScope, lineNumber)
const firstToken = tokens[tokens.length - 1];
let scope = resolveInUnionScope(firstToken, unionScope, lineNumber)

if (!scope) {
console.debug('don\'t found in global scope')
Expand All @@ -52,7 +75,7 @@ function objectScope(tokensSequence: TokensSequence, unionScope: UnionScope, lin
token = token.substring(0, pos2)
}

const member = scope.getMembers().find(s => s.name.localeCompare(token, undefined, { sensitivity: 'accent' }) === 0)
const member = findMember(scope, token)
if (member !== undefined && member.type !== undefined) {
const tokenScope = globalScope.resolveType(member.type)
if (tokenScope !== undefined) {
Expand All @@ -69,25 +92,37 @@ function objectScope(tokensSequence: TokensSequence, unionScope: UnionScope, lin
return scope
}

function resolveInUnionScope(token: string, unionScope: UnionScope, lineNumber: number): Scope | undefined {
function findMember(scope: Scope, token: string): Symbol | undefined {
return scope.getMembers().find(s => s.name.localeCompare(token, undefined, { sensitivity: 'accent' }) === 0)
}

function globalScopeMember(token: string, unionScope: UnionScope, lineNumber: number): Symbol | undefined {
const scopes = unionScope.getScopes(lineNumber);

for (let index = scopes.length - 1; index >= 0; index--) {
const scope = scopes[index]
const member = scope.getMembers().find(s => s.name.localeCompare(token, undefined, { sensitivity: 'accent' }) === 0)
if (member !== undefined) {
if (member.type !== undefined) {
const tokenScope = globalScope.resolveType(member.type)
if (tokenScope !== undefined) {
return tokenScope
}
}
return undefined
const member = findMember(scope, token)
if (member) {
return member
}
}
return undefined
}

function resolveInUnionScope(token: string, unionScope: UnionScope, lineNumber: number): Scope | undefined {
const member = globalScopeMember(token, unionScope, lineNumber)

if (member) {
if (member.type) {
const tokenScope = globalScope.resolveType(member.type)
if (tokenScope) {
return tokenScope
}
}
return undefined
}
}

export {
scopeProvider
}
4 changes: 3 additions & 1 deletion src/languages/bsl/contribution.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { languages } from 'monaco-editor';
import { completionItemProvider } from '../../bsl/features/completionItemProvider'
import { signatureHelpProvider } from '../../bsl/features/signatureHelpProvider'

interface ILangImpl {
conf: languages.LanguageConfiguration;
Expand All @@ -17,6 +18,7 @@ languages.onLanguage(language.id, () => {
import("./configuration").then((module: ILangImpl) => {
languages.setLanguageConfiguration(language.id, module.conf);
languages.setMonarchTokensProvider(language.id, module.language);
languages.registerCompletionItemProvider(language.id, completionItemProvider)
languages.registerCompletionItemProvider(language.id, completionItemProvider);
languages.registerSignatureHelpProvider(language.id, signatureHelpProvider)
});
});
9 changes: 8 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,22 @@ const content: string =
КонецПроцедуры
Процедура ТестУспешно() Экспорт
ЮТест.ОжидаетЧто(1).Равно(1);
Результат = 1;
ЮТест.ОжидаетЧто(Результат).Равно(1);
КонецПроцедуры
Процедура ТестОшибка() Экспорт
ЮТест.ОжидаетЧто(1).Равно(2);
КонецПроцедуры
Процедура ТестСломан() Экспорт
ЮТест.ОжидаетЧто(1).ОтсутствующийМетод(2);
КонецПроцедуры
`;

Expand Down
4 changes: 3 additions & 1 deletion src/scope/Scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ export enum SymbolType {
export interface Symbol {
kind: SymbolType,
name: string,
type?: string
type?: string,
description?: string
}

export interface MethodSymbol extends Symbol {
params: Parameter[],
}
Expand Down
17 changes: 14 additions & 3 deletions src/yaxunit/scope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TypeDefinition, SymbolType, PredefinedType } from "../scope/Scope"
import { TypeDefinition, SymbolType, PredefinedType, MethodSymbol } from "../scope/Scope"
import GlobalScope from "../scope/globalScope"

const scope: TypeDefinition = new PredefinedType('', [
Expand All @@ -14,8 +14,19 @@ const symbols: TypeDefinition[] = [
{
kind: SymbolType.function,
name: 'ОжидаетЧто',
type: 'CommonModule.ЮТУтверждения'
},
params: [
{
name: 'ПроверяемоеЗначение',
type: 'Произвольный',
def: 'Проверяемое фактическое значение'
}, {
name: 'Сообщение',
type: 'Строка',
def: 'Описание проверки, которое будет выведено при возникновении ошибки'
}
],
type: 'CommonModule.ЮТУтверждения'
} as MethodSymbol,
{
kind: SymbolType.function,
name: 'ОжидаетЧтоТаблицаБазы',
Expand Down

0 comments on commit 128895c

Please sign in to comment.