Skip to content

Commit 664a39b

Browse files
committed
👍 Add a priority property to Decoration
1 parent cfdf259 commit 664a39b

File tree

2 files changed

+109
-28
lines changed

2 files changed

+109
-28
lines changed

buffer/decoration.ts

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const cacheKey = "denops_std/buffer/decoration/vimDecorate/rs@1";
99

1010
const PREFIX = "denops_std:buffer:decoration:decorate";
1111

12+
const DEFAULT_DECORATION_PRIORITY = 0;
13+
1214
export type DecorateOptions = {
1315
/**
1416
* Decoration namespace
@@ -33,6 +35,11 @@ export interface Decoration {
3335
* Highlight name
3436
*/
3537
highlight: string;
38+
/**
39+
* Priority for the decoration (higher numbers are displayed on top)
40+
* Defaults to 0 if not specified
41+
*/
42+
priority?: number;
3643
}
3744

3845
/**
@@ -53,6 +60,7 @@ export interface Decoration {
5360
* column: 1,
5461
* length: 10,
5562
* highlight: "Special",
63+
* priority: 10,
5664
* },
5765
* {
5866
* line: 2,
@@ -157,41 +165,50 @@ export function listDecorations(
157165
}
158166
}
159167

160-
function uniq<T>(array: readonly T[]): T[] {
161-
return [...new Set(array)];
162-
}
163-
164168
async function vimDecorate(
165169
denops: Denops,
166170
bufnr: number,
167171
decorations: readonly Decoration[],
168172
options: Readonly<DecorateOptions> = {},
169173
): Promise<void> {
170174
const prefix = options.namespace ?? `${PREFIX}:${denops.name}`;
171-
const toPropType = (n: string) => `${prefix}:${n}`;
175+
const toPropTypeName = (n: string, p: number | undefined) =>
176+
`${prefix}:${p ?? DEFAULT_DECORATION_PRIORITY}:${n}`;
172177
const rs = (denops.context[cacheKey] ?? new Set()) as Set<string>;
173178
denops.context[cacheKey] = rs;
174-
const hs = uniq(decorations.map((v) => v.highlight)).filter((v) =>
175-
!rs.has(v)
176-
);
177-
const decoMap = new Map<string, Set<[number, number, number, number]>>();
179+
const decosToUpdate = Array.from(
180+
new Map(
181+
decorations
182+
.map(({ highlight, priority }) => ({ highlight, priority }))
183+
.map((p) => [toPropTypeName(p.highlight, p.priority), p] as const),
184+
).values(),
185+
).filter((p) => !rs.has(toPropTypeName(p.highlight, p.priority)));
186+
187+
const propsByType = new Map<string, Set<[number, number, number, number]>>();
178188
for (const deco of decorations) {
179-
const propType = toPropType(deco.highlight);
180-
const props = decoMap.get(propType) ?? new Set();
189+
const propTypeName = toPropTypeName(deco.highlight, deco.priority);
190+
const props = propsByType.get(propTypeName) ?? new Set();
181191
props.add([deco.line, deco.column, deco.line, deco.column + deco.length]);
182-
decoMap.set(propType, props);
192+
propsByType.set(propTypeName, props);
183193
}
194+
184195
await batch(denops, async (denops) => {
185-
for (const highlight of hs) {
186-
const propType = toPropType(highlight);
187-
await vimFn.prop_type_add(denops, propType, {
196+
for (const { highlight, priority } of decosToUpdate) {
197+
const propTypeName = toPropTypeName(highlight, priority);
198+
const propOptions: Record<string, unknown> = {
188199
highlight,
189200
combine: false,
190-
});
191-
rs.add(highlight);
201+
priority: priority ?? DEFAULT_DECORATION_PRIORITY,
202+
};
203+
204+
if (!rs.has(propTypeName)) {
205+
await vimFn.prop_type_add(denops, propTypeName, propOptions);
206+
}
207+
rs.add(propTypeName);
192208
}
209+
193210
let id = 1;
194-
for (const [type, props] of decoMap.entries()) {
211+
for (const [type, props] of propsByType.entries()) {
195212
await vimFn.prop_add_list(denops, { bufnr, type, id: id++ }, [...props]);
196213
}
197214
});
@@ -240,14 +257,22 @@ async function vimListDecorations(
240257
type: string;
241258
type_bufnr: number;
242259
}[];
243-
return props
244-
.filter((prop) => prop.type.startsWith(`${prefix}:`))
245-
.map((prop) => ({
246-
line: prop.lnum,
247-
column: prop.col,
248-
length: prop.length,
249-
highlight: prop.type.split(":").pop() as string,
250-
}));
260+
return Promise.all(
261+
props
262+
.filter((prop) => prop.type.startsWith(`${prefix}:`))
263+
.map(async (prop) => {
264+
const propType = await vimFn.prop_type_get(denops, prop.type) as {
265+
priority: number;
266+
};
267+
return ({
268+
line: prop.lnum,
269+
column: prop.col,
270+
length: prop.length,
271+
highlight: prop.type.split(":").pop() as string,
272+
priority: propType.priority,
273+
});
274+
}),
275+
);
251276
}
252277

253278
async function nvimDecorate(
@@ -270,6 +295,7 @@ async function nvimDecorate(
270295
{
271296
end_col: deco.column - 1 + deco.length,
272297
hl_group: deco.highlight,
298+
priority: deco.priority ?? DEFAULT_DECORATION_PRIORITY,
273299
},
274300
);
275301
}
@@ -323,5 +349,6 @@ async function nvimListDecorations(
323349
column: extmark[2] + 1,
324350
length: extmark[3].end_col - extmark[2],
325351
highlight: extmark[3].hl_group,
352+
priority: extmark[3].priority ?? DEFAULT_DECORATION_PRIORITY,
326353
}));
327354
}

buffer/decoration_test.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ test({
5454
length: 5,
5555
lnum: 1,
5656
start: 1,
57-
type: "denops_std:buffer:decoration:decorate:denops-test:Title",
57+
type: "denops_std:buffer:decoration:decorate:denops-test:0:Title",
5858
type_bufnr: 0,
5959
}, {
6060
col: 2,
6161
end: 1,
6262
length: 3,
6363
lnum: 2,
6464
start: 1,
65-
type: "denops_std:buffer:decoration:decorate:denops-test:Search",
65+
type: "denops_std:buffer:decoration:decorate:denops-test:0:Search",
6666
type_bufnr: 0,
6767
}]);
6868

@@ -123,6 +123,54 @@ test({
123123
},
124124
});
125125

126+
test({
127+
mode: "all",
128+
name:
129+
"decorate defines each highlight when multiple decorations have different priorities",
130+
fn: async (denops) => {
131+
const bufnr = await fn.bufnr(denops);
132+
await buffer.replace(denops, bufnr, [
133+
"Hello",
134+
"World",
135+
]);
136+
137+
await decorate(denops, bufnr, [
138+
{
139+
line: 1,
140+
column: 1,
141+
length: 5,
142+
highlight: "Title",
143+
priority: 10,
144+
},
145+
{
146+
line: 2,
147+
column: 1,
148+
length: 5,
149+
highlight: "Title",
150+
priority: 20,
151+
},
152+
]);
153+
154+
const decorations = await listDecorations(denops, bufnr);
155+
assertEquals(decorations, [
156+
{
157+
line: 1,
158+
column: 1,
159+
length: 5,
160+
highlight: "Title",
161+
priority: 10,
162+
},
163+
{
164+
line: 2,
165+
column: 1,
166+
length: 5,
167+
highlight: "Title",
168+
priority: 20,
169+
},
170+
]);
171+
},
172+
});
173+
126174
test({
127175
mode: "all",
128176
name: "listDecorations list decorations defined in the buffer",
@@ -146,6 +194,7 @@ test({
146194
column: 2,
147195
length: 3,
148196
highlight: "Search",
197+
priority: 10,
149198
},
150199
]);
151200
assertEquals(await listDecorations(denops, bufnr), [
@@ -154,12 +203,14 @@ test({
154203
column: 1,
155204
length: 5,
156205
highlight: "Title",
206+
priority: 0,
157207
},
158208
{
159209
line: 2,
160210
column: 2,
161211
length: 3,
162212
highlight: "Search",
213+
priority: 10,
163214
},
164215
]);
165216
},
@@ -188,6 +239,7 @@ test({
188239
column: 2,
189240
length: 3,
190241
highlight: "Search",
242+
priority: 10,
191243
},
192244
]);
193245
await undecorate(denops, bufnr);
@@ -218,6 +270,7 @@ test({
218270
column: 2,
219271
length: 3,
220272
highlight: "Search",
273+
priority: 10,
221274
},
222275
]);
223276
await undecorate(denops, bufnr, 0, 1);
@@ -227,6 +280,7 @@ test({
227280
column: 2,
228281
length: 3,
229282
highlight: "Search",
283+
priority: 10,
230284
},
231285
]);
232286
},

0 commit comments

Comments
 (0)