Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/language/ContextMapperDslModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { ContextMapperDslValidationRegistry } from './validation/ContextMapperDslValidationRegistry.js'
import { ContextMapperValidationProviderRegistry } from './validation/ContextMapperValidationProviderRegistry.js'
import { ContextMapperDslScopeProvider } from './references/ContextMapperDslScopeProvider.js'
import { ContextMapperDslFoldingRangeProvider } from './folding/ContextMapperDslFoldingRageProvider.js'

/**
* Declaration of custom services - add your own service classes here.
Expand Down Expand Up @@ -47,7 +48,8 @@
ValidationRegistry: (services) => new ContextMapperDslValidationRegistry(services)
},
lsp: {
SemanticTokenProvider: (services) => new ContextMapperDslSemanticTokenProvider(services, semanticTokenProviderRegistry)
SemanticTokenProvider: (services) => new ContextMapperDslSemanticTokenProvider(services, semanticTokenProviderRegistry),
FoldingRangeProvider: (services) => new ContextMapperDslFoldingRangeProvider(services)
},
references: {
ScopeProvider: (services) => new ContextMapperDslScopeProvider(services)
Expand Down Expand Up @@ -86,7 +88,7 @@
if (!context.connection) {
// We don't run inside a language server
// Therefore, initialize the configuration provider instantly
shared.workspace.ConfigurationProvider.initialized({})

Check notice on line 91 in src/language/ContextMapperDslModule.ts

View workflow job for this annotation

GitHub Actions / Qodana for JS

Result of method call returning a promise is ignored

Promise returned from initialized is ignored
}
return { shared, ContextMapperDsl }
}
39 changes: 39 additions & 0 deletions src/language/folding/ContextMapperDslFoldingRageProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { DefaultFoldingRangeProvider } from 'langium/lsp'
import { CstNode, LangiumDocument } from 'langium'
import { FoldingRange, FoldingRangeKind } from 'vscode-languageserver-types'

export class ContextMapperDslFoldingRangeProvider extends DefaultFoldingRangeProvider {
// modified version of DefaultFoldingRangeProvider#toFoldingRange
protected override toFoldingRange (document: LangiumDocument, node: CstNode, kind?: string): FoldingRange | undefined {
const range = node.range
let start = range.start
let end = range.end

// Don't generate foldings for nodes that are less than 3 lines
if (end.line - start.line < 2) {
return undefined
}

if (!this.includeLastFoldingLine(node, kind)) { // checks if block is parenthesis/bracket/brace block or comment
// To prevent something like '...}' in the editor when a code block is collapsed, we want to still show the first line of a code block
start = document.textDocument.positionAt(document.textDocument.offsetAt({
line: start.line + 1,
character: 0
}) - 1)

let offsetFromEnd
if (kind === FoldingRangeKind.Comment) {
// So that comments are collapsed like '/*...*/' the end character needs to be at the start of the last line
offsetFromEnd = 0
} else {
// As we don't want to hide the end token like 'if { ... --> } <--', we simply select the end of the previous line as the end position
offsetFromEnd = 1
}
end = document.textDocument.positionAt(document.textDocument.offsetAt({
line: end.line,
character: 0
}) - offsetFromEnd)
}
return FoldingRange.create(start.line, end.line, start.character, end.character, kind)
}
}
92 changes: 92 additions & 0 deletions test/folding/CommentBlockFolding.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { createContextMapperDslServices } from '../../src/language/ContextMapperDslModule.js'
import { FoldingRangeProvider } from 'langium/lsp'
import { clearDocuments, parseHelper } from 'langium/test'
import { ContextMappingModel } from '../../src/language/generated/ast.js'
import { EmptyFileSystem, LangiumDocument } from 'langium'
import { afterEach, beforeAll, describe, expect, test } from 'vitest'
import { parseValidInput } from '../ParsingTestHelper.js'
import { FoldingRangeParams } from 'vscode-languageserver'

let services: ReturnType<typeof createContextMapperDslServices>
let foldingRangeProvider: FoldingRangeProvider
let parse: ReturnType<typeof parseHelper<ContextMappingModel>>
let document: LangiumDocument<ContextMappingModel> | undefined

beforeAll(async () => {
services = createContextMapperDslServices(EmptyFileSystem)
foldingRangeProvider = services.ContextMapperDsl.lsp.FoldingRangeProvider!
parse = parseHelper<ContextMappingModel>(services.ContextMapperDsl)
})

afterEach(async () => {
document && await clearDocuments(services.shared, [document])
})

describe('Comment block folding tests', () => {
test('check folding range of comment block', async () => {
document = await parseValidInput(parse, `
/*
This is a comment block
*/
BoundedContext TestContext
`)

const params = createFoldingRangeParams(document)
const foldingRanges = await foldingRangeProvider.getFoldingRanges(document, params)
expect(foldingRanges).toHaveLength(1)
expect(foldingRanges[0].startLine).toEqual(1)
expect(foldingRanges[0].startCharacter).toEqual(8)
expect(foldingRanges[0].endLine).toEqual(3)
expect(foldingRanges[0].endCharacter).toEqual(0)
})

test('check folding range of multiline comment', async () => {
document = await parseValidInput(parse, `
BoundedContext TestContext {
/* This is a
looooonger
multiline comment
*/
Aggregate TestAggregate
}
`)

const params = createFoldingRangeParams(document)
const foldingRanges = await foldingRangeProvider.getFoldingRanges(document, params)
expect(foldingRanges).toHaveLength(2)
expect(foldingRanges[1].startLine).toEqual(2)
expect(foldingRanges[1].startCharacter).toEqual(20)
expect(foldingRanges[1].endLine).toEqual(5)
expect(foldingRanges[1].endCharacter).toEqual(0)
})

test('check folding range of single-line comment', async () => {
document = await parseValidInput(parse, `
// This is a single-line comment
BoundedContext TestContext
`)

const params = createFoldingRangeParams(document)
const foldingRanges = await foldingRangeProvider.getFoldingRanges(document, params)
expect(foldingRanges).toHaveLength(0)
})

test('check folding range of single line comment block', async () => {
document = await parseValidInput(parse, `
/* This is a single-line comment */
BoundedContext TestContext
`)

const params = createFoldingRangeParams(document)
const foldingRanges = await foldingRangeProvider.getFoldingRanges(document, params)
expect(foldingRanges).toHaveLength(0)
})
})

function createFoldingRangeParams (document: LangiumDocument<ContextMappingModel>): FoldingRangeParams {
return {
textDocument: {
uri: document.uri.path
}
}
}
70 changes: 70 additions & 0 deletions test/folding/DefinitionBlockFolding.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { afterEach, beforeAll, describe, expect, test } from 'vitest'
import { createContextMapperDslServices } from '../../src/language/ContextMapperDslModule.js'
import { EmptyFileSystem, LangiumDocument } from 'langium'
import { FoldingRangeProvider } from 'langium/lsp'
import { clearDocuments, parseHelper } from 'langium/test'
import { ContextMappingModel } from '../../src/language/generated/ast.js'
import { parseValidInput } from '../ParsingTestHelper.js'
import { FoldingRangeParams } from 'vscode-languageserver'

let services: ReturnType<typeof createContextMapperDslServices>
let foldingRangeProvider: FoldingRangeProvider
let parse: ReturnType<typeof parseHelper<ContextMappingModel>>
let document: LangiumDocument<ContextMappingModel> | undefined

beforeAll(async () => {
services = createContextMapperDslServices(EmptyFileSystem)
foldingRangeProvider = services.ContextMapperDsl.lsp.FoldingRangeProvider!
parse = parseHelper<ContextMappingModel>(services.ContextMapperDsl)
})

afterEach(async () => {
document && await clearDocuments(services.shared, [document])
})

describe('Definition block folding tests', () => {
test('check folding range of definition without body', async () => {
document = await parseValidInput(parse, `
BoundedContext TestContext
`)

const params = createFoldingRangeParams(document)
const foldingRanges = await foldingRangeProvider.getFoldingRanges(document, params)
expect(foldingRanges).toHaveLength(0)
})

test('check folding range of definition with one line body', async () => {
document = await parseValidInput(parse, `
BoundedContext TestContext {
}
`)

const params = createFoldingRangeParams(document)
const foldingRanges = await foldingRangeProvider.getFoldingRanges(document, params)
expect(foldingRanges).toHaveLength(0)
})

test('check folding range of definition with two line body', async () => {
document = await parseValidInput(parse, `
BoundedContext TestContext {
type TEAM
}
`)

const params = createFoldingRangeParams(document)
const foldingRanges = await foldingRangeProvider.getFoldingRanges(document, params)
expect(foldingRanges).toHaveLength(1)
expect(foldingRanges[0].startLine).toEqual(1)
expect(foldingRanges[0].startCharacter).toEqual(34)
expect(foldingRanges[0].endLine).toEqual(2)
expect(foldingRanges[0].endCharacter).toEqual(17)
})
})

function createFoldingRangeParams (document: LangiumDocument<ContextMappingModel>): FoldingRangeParams {
return {
textDocument: {
uri: document.uri.path
}
}
}
Loading