Skip to content

Commit

Permalink
refactor: use class for loader
Browse files Browse the repository at this point in the history
  • Loading branch information
uga-rosa committed Dec 7, 2023
1 parent 2346452 commit e400711
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 112 deletions.
18 changes: 5 additions & 13 deletions autoload/denippet/load.vim
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,16 @@ function s:regex_builder(prefix) abort
return regex
endfunction

function denippet#load#base(prefix, from_normal) abort
let pattern = a:from_normal
\ ? '\v^$'
\ : '\v^' . s:regex_builder(a:prefix)
function denippet#load#base(prefix) abort
let pattern = '\v^' . s:regex_builder(a:prefix)
let enabled = v:true
let enabled = enabled && line('$') == 1
let enabled = enabled && getline(1) =~# pattern
return enabled
endfunction

function denippet#load#start(prefix, from_normal) abort
if a:from_normal && col('.') == 1
let line_before_cursor = ''
else
let line_before_cursor = getline('.')[:col('.')-2]
endif
let pattern = a:from_normal
\ ? '\v^\s*$'
\ : '\v^\s*' . s:regex_builder(a:prefix)
function denippet#load#start(prefix) abort
let line_before_cursor = getline('.')[:col('.')-2]
let pattern = '\v^\s*' . s:regex_builder(a:prefix)
return match(line_before_cursor, pattern) > -1
endfunction
183 changes: 95 additions & 88 deletions denops/denippet/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,105 +79,112 @@ function toString(x?: string | string[]): string {
}
}

// Keys are filetypes
const Cell: Record<string, NormalizedSnippet[]> = {};

function setSnippets(
filetype: string | string[],
snippets: NormalizedSnippet[],
): void {
toArray(filetype).forEach((ft) => {
if (Cell[ft] == null) {
Cell[ft] = [];
}
Cell[ft].push(...snippets);
});
}
export class Loader {
cell: Record<string, NormalizedSnippet[]> = {};

export async function getSnippets(
denops: Denops,
ft: string,
fromNormal: boolean,
): Promise<NormalizedSnippet[]> {
const snippets = [
...Cell[ft] ?? [],
...Cell["*"] ?? [],
];
return await asyncFilter(snippets, async (snippet) => {
if (snippet.if == null) {
return true;
} else if (isIfFunc(snippet.if)) {
return Boolean(await snippet.if(denops));
} else if (snippet.if === "base") {
return Boolean(await denops.call("denippet#load#base", snippet.prefix, fromNormal));
} else if (snippet.if === "start") {
return Boolean(await denops.call("denippet#load#start", snippet.prefix, fromNormal));
} else if (!snippet.eval) {
return false;
} else if (snippet.if === "vimscript") {
return Boolean(await denops.call("eval", snippet.eval));
} else {
return Boolean(await denops.call("luaeval", "assert(loadstring(_A[1]))()", [snippet.eval]));
}
});
}
constructor(
public denops: Denops,
) {}

reset(): void {
this.cell = {};
}

set(
filetype: string | string[],
snippets: NormalizedSnippet[],
): void {
toArray(filetype).forEach((ft) => {
if (this.cell[ft] == null) {
this.cell[ft] = [];
}
this.cell[ft].push(...snippets);
});
}

export async function load(
filepath: string,
filetype: string | string[],
): Promise<void> {
const extension = filepath.split(".").pop()!;
let snippets: NormalizedSnippet[] = [];

if (extension === "ts") {
const content = await import(filepath).then((module) => module.snippets);
u.assert(content, is.RecordOf(isTSSnippet));
snippets = Object.entries(content).map(([name, snip]) => ({
...snip,
name,
prefix: toArray(snip.prefix ?? name),
body: async (denops: Denops) => {
return toString(
typeof snip.body == "function" ? await snip.body(denops) : snip.body,
async get(ft: string): Promise<NormalizedSnippet[]> {
const snippets = [
...this.cell[ft] ?? [],
...this.cell["*"] ?? [],
];
return await asyncFilter(snippets, async (snippet) => {
if (snippet.if == null) {
return true;
} else if (isIfFunc(snippet.if)) {
return Boolean(await snippet.if(this.denops));
} else if (snippet.if === "base") {
return Boolean(await this.denops.call("denippet#load#base", snippet.prefix));
} else if (snippet.if === "start") {
return Boolean(await this.denops.call("denippet#load#start", snippet.prefix));
} else if (!snippet.eval) {
return false;
} else if (snippet.if === "vimscript") {
return Boolean(await this.denops.call("eval", snippet.eval));
} else {
return Boolean(
await this.denops.call("luaeval", "assert(loadstring(_A[1]))()", [snippet.eval]),
);
},
description: toString(snip.description),
}));
} else {
const raw = await Deno.readTextFile(filepath);
if (["json", "toml", "yaml"].includes(extension)) {
const content = (extension === "json")
? JSON.parse(raw)
: extension === "toml"
? TOML.parse(raw)
: YAML.parse(raw);
u.assert(content, is.RecordOf(isRawSnippet));
}
});
}

async load(
filepath: string,
filetype: string | string[],
): Promise<void> {
const extension = filepath.split(".").pop()!;
let snippets: NormalizedSnippet[] = [];

if (extension === "ts") {
const content = await import(filepath).then((module) => module.snippets);
u.assert(content, is.RecordOf(isTSSnippet));
snippets = Object.entries(content).map(([name, snip]) => ({
...snip,
name,
prefix: toArray(snip.prefix ?? name),
body: toString(snip.body),
body: async (denops: Denops) => {
return toString(
typeof snip.body == "function" ? await snip.body(denops) : snip.body,
);
},
description: toString(snip.description),
}));
} else if (extension === "code-snippets") {
const content = JSON.parse(raw);
u.assert(content, is.RecordOf(isGlobalSnippet));
for (const [name, snippet] of Object.entries(content)) {
const ft = snippet.scope ? snippet.scope.split(",").map(langToFt) : "*";
const snip = {
...snippet,
} else {
const raw = await Deno.readTextFile(filepath);
if (["json", "toml", "yaml"].includes(extension)) {
const content = (extension === "json")
? JSON.parse(raw)
: extension === "toml"
? TOML.parse(raw)
: YAML.parse(raw);
u.assert(content, is.RecordOf(isRawSnippet));
snippets = Object.entries(content).map(([name, snip]) => ({
...snip,
name,
prefix: toArray(snippet.prefix ?? name),
body: toString(snippet.body),
description: toString(snippet.description),
};
setSnippets(ft, [snip]);
prefix: toArray(snip.prefix ?? name),
body: toString(snip.body),
description: toString(snip.description),
}));
} else if (extension === "code-snippets") {
const content = JSON.parse(raw);
u.assert(content, is.RecordOf(isGlobalSnippet));
for (const [name, snippet] of Object.entries(content)) {
const ft = snippet.scope ? snippet.scope.split(",").map(langToFt) : "*";
const snip = {
...snippet,
name,
prefix: toArray(snippet.prefix ?? name),
body: toString(snippet.body),
description: toString(snippet.description),
};
this.set(ft, [snip]);
}
return;
} else {
throw new Error(`Unknown extension: ${extension}`);
}
return;
} else {
throw new Error(`Unknown extension: ${extension}`);
}
}

setSnippets(filetype, snippets);
this.set(filetype, snippets);
}
}
22 changes: 11 additions & 11 deletions denops/denippet/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { au, Denops, fn, g, lambda, op } from "./deps/denops.ts";
import { is, u } from "./deps/unknownutil.ts";
import { lsputil } from "./deps/lsp.ts";
import { getSnippets, load, NormalizedSnippet } from "./loader.ts";
import { Loader, NormalizedSnippet } from "./loader.ts";
import { Session } from "./session.ts";
import { register } from "./variable.ts";
import { echoerr } from "./util.ts";
Expand All @@ -23,14 +23,14 @@ type SearchResult = {
};

async function searchSnippet(
denops: Denops,
loader: Loader,
): Promise<SearchResult> {
const ctx = await lsputil.LineContext.create(denops);
const ctx = await lsputil.LineContext.create(loader.denops);
const lineBeforeCursor = ctx.text.slice(0, ctx.character);

let bestMatch: SearchResult = {};
const filetype = await op.filetype.get(denops);
(await getSnippets(denops, filetype, false)).forEach((snippet) => {
const filetype = await op.filetype.get(loader.denops);
(await loader.get(filetype)).forEach((snippet) => {
snippet.prefix.forEach((prefix) => {
if (
lineBeforeCursor.endsWith(prefix) &&
Expand All @@ -46,6 +46,7 @@ async function searchSnippet(

export function main(denops: Denops): void {
const session = new Session(denops);
const loader = new Loader(denops);

let timeoutId: ReturnType<typeof setTimeout> | undefined;

Expand Down Expand Up @@ -81,19 +82,19 @@ export function main(denops: Denops): void {
is.OneOf([is.String, is.ArrayOf(is.String)]),
);
try {
await load(filepath, filetype);
await loader.load(filepath, filetype);
} catch (e) {
echoerr(denops, `Failed to load a snippet file ${filepath}.\n${e}`);
}
},

async expandable(): Promise<boolean> {
const { body } = await searchSnippet(denops);
const { body } = await searchSnippet(loader);
return body != null;
},

async expand(): Promise<void> {
const { prefix, body } = await searchSnippet(denops);
const { prefix, body } = await searchSnippet(loader);
if (body == null) {
return;
}
Expand Down Expand Up @@ -177,10 +178,9 @@ export function main(denops: Denops): void {
await session.choice(dir);
},

async getCompleteItems(fromNormalU: unknown): Promise<CompleteItem[]> {
const fromNormal = u.ensure(fromNormalU, is.Boolean);
async getCompleteItems(): Promise<CompleteItem[]> {
const filetype = await op.filetype.get(denops);
return (await getSnippets(denops, filetype, fromNormal)).flatMap((snippet) =>
return (await loader.get(filetype)).flatMap((snippet) =>
snippet.prefix.map((prefix) => ({
word: prefix,
kind: "Snippet",
Expand Down

0 comments on commit e400711

Please sign in to comment.