[WIP]denopsでなんやかんやする備忘録

:::note warn
この記事はスタブです
筆者が `denops` を触ったり触らなかったりするたびに更新される可能性があります
:::

みんなZ〇nnにいっちゃって、Qiitaに `denops` の記事がZ〇nnZ〇nnないのかなしい

# 基本事項

## 最初に始める方法

おそらくふたつある

筆者はゴリラさんの `denops-template.vim` を利用している

https://github.com/skanehira/denops-template.vim

https://github.com/vim-denops/denops-helloworld.vim

## `deno` での `import`

### 現在は `jsr` です (2025-02-16)

もともと `denops` は `denoland` をライブラリパッケージマネージャとして利用していたように見える…が、現在では `jsr` が利用されるのが主流のようです

ただ、古い記事を参照した場合や `Chat-GPT` さんにお伺いを立てたときに、 `denoland` のモジュールを参照するように促されることがしばしばあります

また、必要な `denopsモジュール` が `denoland` の頃と全然違うところに配置されていたりして混乱の原因となりました

これもまた `公式ドキュメント嫁` 事案ではあるものの、英語苦手マンなのできちんと日本語に残します (ほかのだれかがやってくれていそう)

:::note warn
以下のコード中には `std@7.4.0` との記述があります
これは最初にこの備忘録を記述した際の最新のバージョン番号ですので、あとから参照する場合は適宜読み替えをお願いしますね
:::

```typescript
// Denops本体をロード
import { Denops } from "jsr:@denops/std@7.4.0";

// mod.ts など、 `denoland` の頃に存在していた参照のものは、多くはこの方法で取れます
import * as buffer from "jsr:@denops/std@7.4.0/buffer";

// NG - `fn` なんて名前でexportされていないから `*` で取ってくれ！みたいな怒られ方をする
import fn from "jsr:@denops/std@7.4.0/function";

// NG - 個別にこの名前で `export` が記述されているわけではないらしい
import { popup } from "jsr:@denops/std@7.4.0/popup";
```

### で、当たり前ではありますが (ドキュメント参照)

#### jsrドキュメントの初見殺し/不慣れポイント

`typescript` そのものが `関数型言語` の毛色があるため、 `C#` のような、ある意味で完全なオブジェクト指向言語に慣れている人にとってはちょっと読みづらいです

しかしながら、適切に一応はすべてを書いてくれていますので、根気よく探すことで、欲しい機能に到達できるかもしれません

個人的には `symbol list` を右側ペイン、検索バーの下に欲しいです…ください…

https://jsr.io/@denops/std/doc

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/cc0c2b55-5aa5-433e-a55b-dc598e0f84bc.png)

#### 個人的に多様している参照方法

これは個別のシンボルについての詳細ページです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/79d2105c-a306-491a-b82e-eafb3a22a687.png)

なんとここに、 `import` の方法が載ってます、便利ですね
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/46865e73-3797-49c0-b439-ad6381e88bfe.png)

ですので、おそらく `denoland` 時代のコードを提示された場合、そのコードで利用されているモジュール名で `jsr` 内を `grep` し、そこから `import` のコードを得る…のがベターなのかもしれません

# ちょっと小難しい書き方をしたい (記述内容の分離)

:::note info
後述の `UIを書く` と重複する部分があります
:::

すべてを `main.ts` に書くことに対して抵抗のある私は、どういうわけかファイルを分離したかったのです
そして、その場合はこのように記述可能です

```main.ts
import { Denops } from "jsr:@denops/std@7.4.0";
import { isString } from "jsr:@core/unknownutil@4.3.0";
import { showui } from "./ui.ts";

export async function main(denops: Denops): Promise<void> {
  await denops.cmd(
    `command! -nargs=1 Hello call denops#notify("${denops.name}", "hello", [<f-args>])`,
  );
  await denops.cmd(
    `command! -nargs=0 Showui call denops#notify("${denops.name}", "showlspctl", [])`,
  );

  denops.dispatcher = {
    async hello(arg: unknown): Promise<void> {
      if (isString(arg)) {
        console.log("hello", arg);
      }
      await Promise.resolve();
    },
    async showlspctl(): Promise<void> {
      await showui(denops);
    },
  };
}
```

```ui.ts
import * as buffer from "jsr:@denops/std@7.4.0/buffer";
import * as fn from "jsr:@denops/std@7.4.0/function";
import * as popup from "jsr:@denops/std@7.4.0/popup";

export async function showui(denops: Denops): Promise<void> {
  const bufnr = await fn.bufadd(denops, "");
  await fn.bufload(denops, bufnr);
  // Write some text to the buffer
  await buffer.replace(denops, bufnr, ["Hello, world!"]);
  //const choice = await denops.call("popup_menu", "Select an option:", ["Option 1", "Option 2", "Option 3"]);
  await using pw = await popup.open(denops, {
    bufnr,
    relative: "editor",
    width: 100,
    height: 10,
    row: 1,
    col: 1
  })

  await new Promise((resolve) => setTimeout(resolve, 3000));
}
```

# UIを書く

:::note info
ここではあくまで `popup window` を例とします
:::

こちらにも記載がある通り、この方法で簡単に記述ができます

https://jsr.io/@denops/std/doc/popup

```main.ts
import { Denops } from "jsr:@denops/std@7.4.0";
import { isString } from "jsr:@core/unknownutil@4.3.0";
import * as buffer from "jsr:@denops/std@7.4.0/buffer";
import * as fn from "jsr:@denops/std@7.4.0/function";
import * as popup from "jsr:@denops/std@7.4.0/popup";

export async function main(denops: Denops): Promise<void> {
  await denops.cmd(
    `command! -nargs=1 Hello call denops#notify("${denops.name}", "hello", [<f-args>])`,
  );
  await denops.cmd(
    `command! -nargs=0 Showui call denops#notify("${denops.name}", "showlspctl", [])`,
  );

  denops.dispatcher = {
    async hello(arg: unknown): Promise<void> {
      if (isString(arg)) {
        console.log("hello", arg);
      }
      await Promise.resolve();
    },
    async function showui(denops: Denops): Promise<void> {
      const bufnr = await fn.bufadd(denops, "");
      await fn.bufload(denops, bufnr);
      await buffer.replace(denops, bufnr, ["Hello, world!"]);
      await using pw = await popup.open(denops, {
        bufnr,
        relative: "editor",
        width: 100,
        height: 10,
        row: 1,
        col: 1
      })

      await new Promise((resolve) => setTimeout(resolve, 3000));
    }
  };
}
```

これはこのように表示してくれます
きちんと `popup window (floating window)` として表示されていることがわかります

> 私の環境では青背景はポップアップです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/be689287-8354-494f-a593-a1867d5eed5d.png)

# さいごに

いったん以上です

自力で全部書く