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
79 changes: 53 additions & 26 deletions buffer/decoration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const cacheKey = "denops_std/buffer/decoration/vimDecorate/rs@1";

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

const DEFAULT_DECORATION_PRIORITY = 0;

export type DecorateOptions = {
/**
* Decoration namespace
Expand All @@ -33,6 +35,11 @@ export interface Decoration {
* Highlight name
*/
highlight: string;
/**
* Priority for the decoration (higher numbers are displayed on top)
* Defaults to 0 if not specified
*/
priority?: number;
}

/**
Expand All @@ -53,6 +60,7 @@ export interface Decoration {
* column: 1,
* length: 10,
* highlight: "Special",
* priority: 10,
* },
* {
* line: 2,
Expand Down Expand Up @@ -157,41 +165,50 @@ export function listDecorations(
}
}

function uniq<T>(array: readonly T[]): T[] {
return [...new Set(array)];
}

async function vimDecorate(
denops: Denops,
bufnr: number,
decorations: readonly Decoration[],
options: Readonly<DecorateOptions> = {},
): Promise<void> {
const prefix = options.namespace ?? `${PREFIX}:${denops.name}`;
const toPropType = (n: string) => `${prefix}:${n}`;
const toPropTypeName = (n: string, p: number | undefined) =>
`${prefix}:${p ?? DEFAULT_DECORATION_PRIORITY}:${n}`;
const rs = (denops.context[cacheKey] ?? new Set()) as Set<string>;
denops.context[cacheKey] = rs;
const hs = uniq(decorations.map((v) => v.highlight)).filter((v) =>
!rs.has(v)
);
const decoMap = new Map<string, Set<[number, number, number, number]>>();
const decosToUpdate = Array.from(
new Map(
decorations
.map(({ highlight, priority }) => ({ highlight, priority }))
.map((p) => [toPropTypeName(p.highlight, p.priority), p] as const),
).values(),
).filter((p) => !rs.has(toPropTypeName(p.highlight, p.priority)));

const propsByType = new Map<string, Set<[number, number, number, number]>>();
for (const deco of decorations) {
const propType = toPropType(deco.highlight);
const props = decoMap.get(propType) ?? new Set();
const propTypeName = toPropTypeName(deco.highlight, deco.priority);
const props = propsByType.get(propTypeName) ?? new Set();
props.add([deco.line, deco.column, deco.line, deco.column + deco.length]);
decoMap.set(propType, props);
propsByType.set(propTypeName, props);
}

await batch(denops, async (denops) => {
for (const highlight of hs) {
const propType = toPropType(highlight);
await vimFn.prop_type_add(denops, propType, {
for (const { highlight, priority } of decosToUpdate) {
const propTypeName = toPropTypeName(highlight, priority);
const propOptions: Record<string, unknown> = {
highlight,
combine: false,
});
rs.add(highlight);
priority: priority ?? DEFAULT_DECORATION_PRIORITY,
};

if (!rs.has(propTypeName)) {
await vimFn.prop_type_add(denops, propTypeName, propOptions);
}
rs.add(propTypeName);
}

let id = 1;
for (const [type, props] of decoMap.entries()) {
for (const [type, props] of propsByType.entries()) {
await vimFn.prop_add_list(denops, { bufnr, type, id: id++ }, [...props]);
}
});
Expand Down Expand Up @@ -240,14 +257,22 @@ async function vimListDecorations(
type: string;
type_bufnr: number;
}[];
return props
.filter((prop) => prop.type.startsWith(`${prefix}:`))
.map((prop) => ({
line: prop.lnum,
column: prop.col,
length: prop.length,
highlight: prop.type.split(":").pop() as string,
}));
return Promise.all(
props
.filter((prop) => prop.type.startsWith(`${prefix}:`))
.map(async (prop) => {
const propType = await vimFn.prop_type_get(denops, prop.type) as {
priority: number;
};
return ({
line: prop.lnum,
column: prop.col,
length: prop.length,
highlight: prop.type.split(":").pop() as string,
priority: propType.priority,
});
}),
);
}

async function nvimDecorate(
Expand All @@ -270,6 +295,7 @@ async function nvimDecorate(
{
end_col: deco.column - 1 + deco.length,
hl_group: deco.highlight,
priority: deco.priority ?? DEFAULT_DECORATION_PRIORITY,
},
);
}
Expand Down Expand Up @@ -323,5 +349,6 @@ async function nvimListDecorations(
column: extmark[2] + 1,
length: extmark[3].end_col - extmark[2],
highlight: extmark[3].hl_group,
priority: extmark[3].priority ?? DEFAULT_DECORATION_PRIORITY,
}));
}
58 changes: 56 additions & 2 deletions buffer/decoration_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ test({
length: 5,
lnum: 1,
start: 1,
type: "denops_std:buffer:decoration:decorate:denops-test:Title",
type: "denops_std:buffer:decoration:decorate:denops-test:0:Title",
type_bufnr: 0,
}, {
col: 2,
end: 1,
length: 3,
lnum: 2,
start: 1,
type: "denops_std:buffer:decoration:decorate:denops-test:Search",
type: "denops_std:buffer:decoration:decorate:denops-test:0:Search",
type_bufnr: 0,
}]);

Expand Down Expand Up @@ -123,6 +123,54 @@ test({
},
});

test({
mode: "all",
name:
"decorate defines each highlight when multiple decorations have different priorities",
fn: async (denops) => {
const bufnr = await fn.bufnr(denops);
await buffer.replace(denops, bufnr, [
"Hello",
"World",
]);

await decorate(denops, bufnr, [
{
line: 1,
column: 1,
length: 5,
highlight: "Title",
priority: 10,
},
{
line: 2,
column: 1,
length: 5,
highlight: "Title",
priority: 20,
},
]);

const decorations = await listDecorations(denops, bufnr);
assertEquals(decorations, [
{
line: 1,
column: 1,
length: 5,
highlight: "Title",
priority: 10,
},
{
line: 2,
column: 1,
length: 5,
highlight: "Title",
priority: 20,
},
]);
},
});

test({
mode: "all",
name: "listDecorations list decorations defined in the buffer",
Expand All @@ -146,6 +194,7 @@ test({
column: 2,
length: 3,
highlight: "Search",
priority: 10,
},
]);
assertEquals(await listDecorations(denops, bufnr), [
Expand All @@ -154,12 +203,14 @@ test({
column: 1,
length: 5,
highlight: "Title",
priority: 0,
},
{
line: 2,
column: 2,
length: 3,
highlight: "Search",
priority: 10,
},
]);
},
Expand Down Expand Up @@ -188,6 +239,7 @@ test({
column: 2,
length: 3,
highlight: "Search",
priority: 10,
},
]);
await undecorate(denops, bufnr);
Expand Down Expand Up @@ -218,6 +270,7 @@ test({
column: 2,
length: 3,
highlight: "Search",
priority: 10,
},
]);
await undecorate(denops, bufnr, 0, 1);
Expand All @@ -227,6 +280,7 @@ test({
column: 2,
length: 3,
highlight: "Search",
priority: 10,
},
]);
},
Expand Down
Loading