Skip to content
This repository was archived by the owner on Nov 6, 2018. It is now read-only.

Commit 6b30e52

Browse files
committed
fix(decorations): no providers yields null, skip errors, add tests
1 parent 80db65e commit 6b30e52

File tree

2 files changed

+183
-15
lines changed

2 files changed

+183
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import * as assert from 'assert'
2+
import { of, throwError } from 'rxjs'
3+
import { TestScheduler } from 'rxjs/testing'
4+
import { Position } from 'vscode-languageserver-types'
5+
import { TextDocumentDecoration, TextDocumentDecorationParams } from '../../protocol'
6+
import { getDecorations, ProvideTextDocumentDecorationSignature } from './decoration'
7+
import { FIXTURE as COMMON_FIXTURE } from './textDocument.test'
8+
9+
// const DELAY = 100 // msec
10+
11+
const FIXTURE = {
12+
...COMMON_FIXTURE,
13+
TextDocumentDecorationParams: { textDocument: { uri: 'file:///f' } } as TextDocumentDecorationParams,
14+
}
15+
16+
const FIXTURE_RESULT: TextDocumentDecoration[] | null = [
17+
{
18+
range: { start: Position.create(1, 2), end: Position.create(3, 4) },
19+
backgroundColor: 'red',
20+
},
21+
]
22+
23+
const scheduler = () => new TestScheduler((a, b) => assert.deepStrictEqual(a, b))
24+
25+
describe('getDecorations', () => {
26+
describe('0 providers', () => {
27+
it('returns null', () =>
28+
scheduler().run(({ cold, expectObservable }) =>
29+
expectObservable(
30+
getDecorations(
31+
cold<ProvideTextDocumentDecorationSignature[]>('-a-|', { a: [] }),
32+
FIXTURE.TextDocumentDecorationParams
33+
)
34+
).toBe('-a-|', {
35+
a: null,
36+
})
37+
))
38+
})
39+
40+
describe('1 provider', () => {
41+
it('returns null result from provider', () =>
42+
scheduler().run(({ cold, expectObservable }) =>
43+
expectObservable(
44+
getDecorations(
45+
cold<ProvideTextDocumentDecorationSignature[]>('-a-|', { a: [() => of(null)] }),
46+
FIXTURE.TextDocumentDecorationParams
47+
)
48+
).toBe('-a-|', {
49+
a: null,
50+
})
51+
))
52+
53+
it('returns result from provider', () =>
54+
scheduler().run(({ cold, expectObservable }) =>
55+
expectObservable(
56+
getDecorations(
57+
cold<ProvideTextDocumentDecorationSignature[]>('-a-|', {
58+
a: [() => of(FIXTURE_RESULT)],
59+
}),
60+
FIXTURE.TextDocumentDecorationParams
61+
)
62+
).toBe('-a-|', {
63+
a: FIXTURE_RESULT,
64+
})
65+
))
66+
})
67+
68+
describe('2 providers', () => {
69+
it('returns null result if both providers return null', () =>
70+
scheduler().run(({ cold, expectObservable }) =>
71+
expectObservable(
72+
getDecorations(
73+
cold<ProvideTextDocumentDecorationSignature[]>('-a-|', {
74+
a: [() => of(null), () => of(null)],
75+
}),
76+
FIXTURE.TextDocumentDecorationParams
77+
)
78+
).toBe('-a-|', {
79+
a: null,
80+
})
81+
))
82+
83+
it('omits null result from 1 provider', () =>
84+
scheduler().run(({ cold, expectObservable }) =>
85+
expectObservable(
86+
getDecorations(
87+
cold<ProvideTextDocumentDecorationSignature[]>('-a-|', {
88+
a: [() => of(FIXTURE_RESULT), () => of(null)],
89+
}),
90+
FIXTURE.TextDocumentDecorationParams
91+
)
92+
).toBe('-a-|', {
93+
a: FIXTURE_RESULT,
94+
})
95+
))
96+
97+
it('skips errors from providers', () =>
98+
scheduler().run(({ cold, expectObservable }) =>
99+
expectObservable(
100+
getDecorations(
101+
cold<ProvideTextDocumentDecorationSignature[]>('-a-|', {
102+
a: [() => of(FIXTURE_RESULT), () => throwError('error')],
103+
}),
104+
FIXTURE.TextDocumentDecorationParams
105+
)
106+
).toBe('-a-|', {
107+
a: FIXTURE_RESULT,
108+
})
109+
))
110+
111+
it('merges results from providers', () =>
112+
scheduler().run(({ cold, expectObservable }) =>
113+
expectObservable(
114+
getDecorations(
115+
cold<ProvideTextDocumentDecorationSignature[]>('-a-|', {
116+
a: [() => of(FIXTURE_RESULT), () => of(FIXTURE_RESULT)],
117+
}),
118+
FIXTURE.TextDocumentDecorationParams
119+
)
120+
).toBe('-a-|', {
121+
a: [...FIXTURE_RESULT!, ...FIXTURE_RESULT!],
122+
})
123+
))
124+
})
125+
126+
describe('multiple emissions', () => {
127+
it('returns stream of results', () =>
128+
scheduler().run(({ cold, expectObservable }) =>
129+
expectObservable(
130+
getDecorations(
131+
cold<ProvideTextDocumentDecorationSignature[]>('-a-b-|', {
132+
a: [() => of(FIXTURE_RESULT)],
133+
b: [() => of(null)],
134+
}),
135+
FIXTURE.TextDocumentDecorationParams
136+
)
137+
).toBe('-a-b-|', {
138+
a: FIXTURE_RESULT,
139+
b: null,
140+
})
141+
))
142+
})
143+
})

src/environment/providers/decoration.ts

+40-15
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,47 @@ export class TextDocumentDecorationProviderRegistry extends TextDocumentFeatureP
1515
ProvideTextDocumentDecorationSignature
1616
> {
1717
public getDecorations(params: TextDocumentDecorationParams): Observable<TextDocumentDecoration[] | null> {
18-
return this.providers
19-
.pipe(
20-
switchMap(providers =>
21-
combineLatest(
22-
providers.map(provider =>
23-
provider(params).pipe(
24-
map(results => (results === null ? [] : compact(results))),
25-
catchError(error => {
26-
console.error(error)
27-
return [[]]
28-
})
29-
)
18+
return getDecorations(this.providers, params)
19+
}
20+
}
21+
22+
/**
23+
* Returns an observable that emits all decorations whenever any of the last-emitted set of providers emits
24+
* decorations.
25+
*
26+
* Most callers should use TextDocumentDecorationProviderRegistry, which sources decorations from the set of
27+
* registered providers.
28+
*/
29+
export function getDecorations(
30+
providers: Observable<ProvideTextDocumentDecorationSignature[]>,
31+
params: TextDocumentDecorationParams
32+
): Observable<TextDocumentDecoration[] | null> {
33+
return providers
34+
.pipe(
35+
switchMap(providers => {
36+
if (providers.length === 0) {
37+
return [null]
38+
}
39+
return combineLatest(
40+
providers.map(provider =>
41+
provider(params).pipe(
42+
map(results => (results === null ? [] : compact(results))),
43+
catchError(error => {
44+
console.error(error)
45+
return [null]
46+
})
3047
)
3148
)
3249
)
33-
)
34-
.pipe(map(results => flatten(results)))
35-
}
50+
})
51+
)
52+
.pipe(
53+
map(results => {
54+
if (results === null) {
55+
return null
56+
}
57+
const merged = flatten(compact(results))
58+
return merged.length === 0 ? null : merged
59+
})
60+
)
3661
}

0 commit comments

Comments
 (0)