Skip to content

Commit

Permalink
Merge pull request #11 from vim-denops/async-request
Browse files Browse the repository at this point in the history
Add 'denops#promise({plugin}, {func}, {args})` to call denops APIs asynchronously
  • Loading branch information
lambdalisue committed Feb 23, 2021
2 parents 5c8445a + 76f5c94 commit 4c45ce9
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 2 deletions.
10 changes: 10 additions & 0 deletions autoload/denops.vim
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ function! denops#request(plugin, method, params) abort
return denops#server#request('dispatch', [a:plugin, a:method, a:params])
endfunction

function! denops#promise(plugin, method, params) abort
return denops#promise#new(funcref('s:promise_start', [a:plugin, a:method, a:params]))
endfunction

function! s:promise_start(plugin, method, params, resolve, reject) abort
let success = denops#callback#add(a:resolve)
let failure = denops#callback#add(a:reject)
return denops#server#request('dispatchAsync', [a:plugin, a:method, a:params, success, failure])
endfunction

" DEPRECATED
function! denops#register(plugin, script) abort
call denops#error(join([
Expand Down
19 changes: 19 additions & 0 deletions autoload/denops/callback.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
let s:registry = {}

function! denops#callback#add(callback) abort
let id = sha256(string(get(a:callback, 'func')))
let s:registry[id] = a:callback
return id
endfunction

function! denops#callback#call(id, ...) abort
if !has_key(s:registry, a:id)
throw printf('No callback function for %s exist', a:id)
endif
call call(s:registry[a:id], a:000)
silent unlet s:registry[a:id]
endfunction

function! denops#callback#clear() abort
let s:registry = {}
endfunction
2 changes: 1 addition & 1 deletion denops/denops/cli/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const conn = await Deno.connect(address);

// Create host and start communication
const createHost = mode === "vim" ? createVim : createNeovim;
const service = new Service();
const host = createHost(conn, conn);
const service = new Service(host);
host.registerService(service);

// Load plugins
Expand Down
33 changes: 33 additions & 0 deletions denops/denops/host/nvim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,39 @@ class Neovim extends AbstractHost {
}
return await service.dispatch(name, fn, args);
},

async dispatchAsync(
name: unknown,
fn: unknown,
args: unknown,
success: unknown,
failure: unknown,
): Promise<unknown> {
if (typeof name !== "string") {
throw new Error(
`'name' in 'dispatchAsync()' of host must be a string`,
);
}
if (typeof fn !== "string") {
throw new Error(`'fn' in 'dispatchAsync()' of host must be a string`);
}
if (!Array.isArray(args)) {
throw new Error(
`'args' in 'dispatchAsync()' of host must be an array`,
);
}
if (typeof success !== "string") {
throw new Error(
`'success' in 'dispatchAsync()' of host must be a string`,
);
}
if (typeof failure !== "string") {
throw new Error(
`'failure' in 'dispatchAsync()' of host must be a string`,
);
}
return await service.dispatchAsync(name, fn, args, success, failure);
},
};
this.#session.extendDispatcher(dispatcher);
}
Expand Down
25 changes: 25 additions & 0 deletions denops/denops/host/vim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ async function dispatch(service: Service, expr: unknown): Promise<unknown> {
if (isDispatchMessage(expr)) {
const [_, name, fn, args] = expr;
return await service.dispatch(name, fn, args);
} else if (isDispatchAsyncMessage(expr)) {
const [_, name, fn, args, success, failure] = expr;
return await service.dispatchAsync(name, fn, args, success, failure);
} else {
throw new Error(
`Unexpected JSON channel message is received: ${JSON.stringify(expr)}`,
Expand All @@ -63,6 +66,15 @@ async function dispatch(service: Service, expr: unknown): Promise<unknown> {

type DispatchMessage = ["dispatch", string, string, unknown[]];

type DispatchAsyncMessage = [
"dispatchAsync",
string,
string,
unknown[],
string,
string,
];

function isDispatchMessage(data: unknown): data is DispatchMessage {
return (
Array.isArray(data) &&
Expand All @@ -73,3 +85,16 @@ function isDispatchMessage(data: unknown): data is DispatchMessage {
Array.isArray(data[3])
);
}

function isDispatchAsyncMessage(data: unknown): data is DispatchAsyncMessage {
return (
Array.isArray(data) &&
data.length === 6 &&
data[0] === "dispatchAsync" &&
typeof data[1] === "string" &&
typeof data[2] === "string" &&
Array.isArray(data[3]) &&
typeof data[4] === "string" &&
typeof data[5] === "string"
);
}
21 changes: 20 additions & 1 deletion denops/denops/service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Session } from "./deps.ts";
import { Host } from "./host/mod.ts";

export class Service {
#plugins: { [key: string]: Session };
#host: Host;

constructor() {
constructor(host: Host) {
this.#plugins = {};
this.#host = host;
}

static register(service: Service, name: string, session: Session): void {
Expand All @@ -24,4 +27,20 @@ export class Service {
throw `${e.stack ?? e.toString()}`;
}
}

dispatchAsync(
name: string,
fn: string,
args: unknown[],
success: string, // Callback ID
failure: string, // Callback ID
): Promise<void> {
this.dispatch(name, fn, args)
.then((r) => this.#host.call("denops#callback#call", success, r))
.catch((e) => this.#host.call("denops#callback#call", failure, e))
.catch((e) => {
console.error(`${e.stack ?? e.toString()}`);
});
return Promise.resolve();
}
}

0 comments on commit 4c45ce9

Please sign in to comment.