neovim (vim) のヘルプコマンドの走り書き その1

  • https://neovim.io/doc/user/helphelp.html#helphelp
  • 引数なしで :help コマンドを打つと helpfile オプションで指定されたファイルが開く。
  • 通常は help.txt が開くと思う。
  • 以下のコマンドで helpfile オプションの値を確認できる。
  • また、タグが指定された場合には、 runtimepath オプションで指定されたディレクトリの doc/tags ファイルの中から検索を行う。
  • この場合も同じく、 :set runtimepath? で設定されている値を確認できる。
    • ヘルプウィンドウの初期高さは helpheight オプションで変更できる。デフォルトは20。

  • ヘルプファイルを見ていて、特定の項目にジャンプする方法は2つある。
  • CTRL-] コマンドを使う。
    • コマンド名、またはオプション名の上にカーソルを移動し、 CTRL-] でその項目にジャンプできる。
  • :ta {subject} コマンドを使う。
    • ジャンプ先から戻るには CTRL-T または CTRL-O コマンドを使う。

  • 特定のトピックについてヘルプファイルを全検索したい場合がある。(大体そう)
  • その場合、次の手順でいい感じに検索ができる。
  • lh {subject} コマンドでヘルプを検索。
    • この場合、grep検索ができる。
    • ロケーションリストで該当箇所を記憶してくれる。
  • :lopen コマンドでロケーションリストを表示する。
  • 各項目にいい感じにジャンプできる。

Clojure Leiningen その5

テスト

  • lein test コマンドでテストを実行。
  • 例。
vagrant@ubuntu2204:~/source/temp$ lein test

lein test temp.core-test

lein test :only temp.core-test/a-test

FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Subprocess failed (exit code: 1)
  • ↑ テストが一件落ちていると警告が出ている。
  • ↑ この場合、テストが少ないので問題ないが、テストが増えてくると時間がかかるようになってくる。名前空間を指定すれば、そこだけテストを実行できる。
  • 例。
vagrant@ubuntu2204:~/source/temp$ lein test temp.core-test

lein test temp.core-test

lein test :only temp.core-test/a-test

FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Subprocess failed (exit code: 1)
  • 存在しない名前空間を指定したらどうなるんだろう?
vagrant@ubuntu2204:~/source/temp$ lein test temp.core

lein test user

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
  • エラーは出ないので注意。代わりに(?) Ran 0 tests とメッセージが出る。
  • REPLで clojure.test/run-tests を使ってもテストを実行できる。この場合、JVMをいちいち起動しないので高速にテストを実行できる。
vagrant@ubuntu2204:~/source/temp$ lein repl
nREPL server started on port 38937 on host 127.0.0.1 - nrepl://127.0.0.1:38937
REPL-y 0.5.1, nREPL 0.9.0
Clojure 1.11.1
OpenJDK 64-Bit Server VM 17.0.5+8-LTS
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

temp.core=> (use 'clojure.test)
nil
temp.core=> (clojure.test/run-tests)

Testing temp.core

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
{:test 0, :pass 0, :fail 0, :error 0, :type :summary}
temp.core=> 
  • Ran 0 tests と言われた。
  • よく見ると実行しているテストの名前空間がテストの名前空間ではない。
  • 名前空間を指定するとうまく実行できた。どうやらデフォルトではREPLで今いる名前空間のテストを実行するみたい。
temp.core=> (require 'temp.core-test)
nil
temp.core=> (clojure.test/run-tests 'temp.core-test)

Testing temp.core-test

FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
{:test 1, :pass 0, :fail 1, :error 0, :type :summary}
temp.core=> 
  • run-tests は繰り返し実行する時間の短縮にはなるが、テストコードを変更した場合(またはその他プロダクションコードを変更した場合も)REPLの世界に変更を取り込まないと反映されない。そのため、変更の取り込みを簡単に行う仕組みを別途整える必要がある。

Clojure Leiningen その4

コードの実行

  • https://codeberg.org/leiningen/leiningen/src/branch/stable/doc/TUTORIAL.md#running-code
  • REPLを実行するには lein repl を使う。
  • REPLを上記のコマンドで実行すると、プロジェクトのコンテキストと共にコードを実行する対話型プロンプトが起動する。
  • :dependencies に記載したライブラリや、プロジェクトのコードを呼び出し動作を確認することができる。
  • 実行例。
vagrant@ubuntu2204:~/source/temp$ lein repl
nREPL server started on port 46115 on host 127.0.0.1 - nrepl://127.0.0.1:46115
REPL-y 0.5.1, nREPL 0.9.0
Clojure 1.11.1
OpenJDK 64-Bit Server VM 17.0.5+8-LTS
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

temp.core=> (foo "bookstore")
bookstore Hello, World!
nil
temp.core=> (require '[clj-http.client :as http])
nil
temp.core=> (def response (http/get "https://leiningen.org"))
#'temp.core/response
temp.core=> (keys response)
(:cached :request-time :repeatable? :protocol-version :streaming? :http-client :chunked? :reason-phrase :headers :orig-content-encoding :status :length :body :trace-redirects)
temp.core=> (:status response)
200
temp.core=> (:headers response)
{"Server" "Apache", "Upgrade" "h2", "Content-Type" "text/html", "Content-Length" "2506", "Permissions-Policy" "interest-cohort=()", "Strict-Transport-Security" "max-age=31536000", "Connection" "Upgrade, close", "Accept-Ranges" "bytes", "Expires" "Sun, 13 Nov 2022 11:29:52 GMT", "ETag" "\"1e19-5e5e706584773-gzip\"", "Date" "Sun, 13 Nov 2022 11:19:52 GMT", "Vary" "Accept-Encoding,User-Agent", "Last-Modified" "Wed, 10 Aug 2022 18:14:50 GMT", "Cache-Control" "max-age=600"}
  • REPLではなく、エントリポイントを直接実行する場合は lein run コマンドを使う。
  • lein run コマンドを使う場合には project.clj の :main-main 関数が定義されている名前空間を指定する。
  • 例えばこう。
(defproject temp "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :main temp.core
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [clj-http "3.12.3"]]
  :repl-options {:init-ns temp.core})
  • temp.core名前空間のコードはこうなっている。
(ns temp.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))

(defn -main "Application entry point" [] (foo "bookstore"))
  • 実行してみる。
vagrant@ubuntu2204:~/source/temp$ lein run
bookstore Hello, World!

Clojure Leiningen その3

ライブラリの検索について

vagrant@ubuntu2204:~/source/temp$ lein search clj-http
Searching central ...
[pro.juxt.clojars-mirrors.clj-http/clj-http "3.12.2"]
Searching clojars ...
[clj-http "3.12.3"]
  A Clojure HTTP library wrapping the Apache HttpComponents client.
[clj-http-fake "1.0.3"]
  Helper for faking clj-http requests in testing, like Ruby's fakeweb.
[org.sharetribe/aws-sig4 "0.1.4"]
  Middleware to add AWS signature v4 signing to clj-http requests.
[clj-oauth2 "0.2.0"]
  clj-http and ring middlewares for OAuth 2.0
[clj-http-hystrix "0.1.6"]
  A Clojure library to wrap clj-http requests as hystrix commands
[martian-clj-http "0.1.17-SNAPSHOT"]
  clj-http implementation for martian
[stuarth/clj-oauth2 "0.3.2"]
  clj-http and ring middlewares for OAuth 2.0
[org.clojars.osbert/clj-oauth2 "0.1.9"]
  clj-http and ring middlewares for OAuth 2.0
[b-ryan/clj-http-mock "0.6.0"]
  Mock responses to clj-http requests.
[org.clj-commons/clj-http-lite "1.0.13"]
  A lite version of clj-http that uses the jre's HttpURLConnection
[clj-http-mock "0.4.0"]
  Mock responses to clj-http requests.
[onaio/clj-oauth2 "0.3.2"]
  clj-http and ring middlewares for OAuth 2.0
[wkf/clj-http "2.0.0"]
  A Clojure HTTP library wrapping the Apache HttpComponents client.
[com.gfredericks.forks.clj-http-fake/clj-http-fake "1.0.3-37e38b42"]
  Helper for faking clj-http requests. For testing. You monster.
[com.github.oliyh/martian-clj-http "0.1.22-SNAPSHOT"]
  clj-http implementation for martian
[opentable/clj-http "3.0.0-beta-1"]
  A Clojure HTTP library wrapping the Apache HttpComponents client.
[mavericklou/clj-oauth2 "0.5.2"]
  clj-http and ring middlewares for OAuth 2.0
[twosigma/clj-http "3.9.1-ts2"]
  A Clojure HTTP library wrapping the Apache HttpComponents client.
[thirtyspokes/hindrance "1.1.0"]
  A convenience wrapper for using OAuth JWT credentials flow with clj-http.
[racehub/clj-http "1.0.1"]
  A Clojure HTTP library wrapping the Apache HttpComponents client.
[org.clojars.ub-hyleung/clj-http-mock "0.5.0"]
  Mock responses to clj-http requests.
[com.farmlogs/looper "0.3.0"]
  Drop-in clj-http replacement with retries
[emil0r/clj-oauth2 "0.6.0"]
  clj-http and ring middlewares for OAuth 2.0 [fork]
[sudharsh/clj-oauth2 "0.5.3"]
  clj-http and ring middlewares for OAuth 2.0

依存関係の定義について

  • 依存関係の定義は project.clj:dependencies ベクターで行う。
  • :dependencies には [<グループID/アーティファクトID> "<バージョン>"] と書く。(Mavenと同じ感覚。)
  • グループIDとアーティファクトIDが同じであれば、2つ書く必要がなく、グループIDを省略していい。
  • 例えば clj-http の場合はグループIDとアーティファクトIDが同じなので [clj-http "3.12.3"] と書ける。
  • leinは :dependencies を見て、依存先をダウンロードする。
  • 明示的にダウンロードしたい場合は lein deps コマンドを使う。

やってみる

  • clj-http を依存先に追加してみる。
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [clj-http "3.12.3"]]
  • lein depsclj-http をダウンロードする。
vagrant@ubuntu2204:~/source/temp$ lein deps
Retrieving clj-http/clj-http/3.12.3/clj-http-3.12.3.pom from clojars
Retrieving org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.pom from central
Retrieving org/apache/httpcomponents/httpcomponents-core/4.4.14/httpcomponents-core-4.4.14.pom from central
Retrieving org/apache/httpcomponents/httpcomponents-parent/11/httpcomponents-parent-11.pom from central
Retrieving org/apache/apache/21/apache-21.pom from central
Retrieving org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.pom from central
Retrieving org/apache/httpcomponents/httpcomponents-client/4.5.13/httpcomponents-client-4.5.13.pom from central
Retrieving org/apache/httpcomponents/httpcore/4.4.13/httpcore-4.4.13.pom from central
Retrieving org/apache/httpcomponents/httpcomponents-core/4.4.13/httpcomponents-core-4.4.13.pom from central
Retrieving org/apache/httpcomponents/httpclient-cache/4.5.13/httpclient-cache-4.5.13.pom from central
Retrieving org/apache/httpcomponents/httpasyncclient/4.1.4/httpasyncclient-4.1.4.pom from central
Retrieving org/apache/httpcomponents/httpcomponents-asyncclient/4.1.4/httpcomponents-asyncclient-4.1.4.pom from central
Retrieving org/apache/httpcomponents/httpcore/4.4.10/httpcore-4.4.10.pom from central
Retrieving org/apache/httpcomponents/httpcomponents-core/4.4.10/httpcomponents-core-4.4.10.pom from central
Retrieving org/apache/httpcomponents/httpcore-nio/4.4.10/httpcore-nio-4.4.10.pom from central
Retrieving org/apache/httpcomponents/httpclient/4.5.6/httpclient-4.5.6.pom from central
Retrieving org/apache/httpcomponents/httpcomponents-client/4.5.6/httpcomponents-client-4.5.6.pom from central
Retrieving org/apache/httpcomponents/httpmime/4.5.13/httpmime-4.5.13.pom from central
Retrieving commons-codec/commons-codec/1.15/commons-codec-1.15.pom from central
Retrieving org/apache/commons/commons-parent/52/commons-parent-52.pom from central
Retrieving org/apache/apache/23/apache-23.pom from central
Retrieving commons-io/commons-io/2.8.0/commons-io-2.8.0.pom from central
Retrieving org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.jar from central
Retrieving org/apache/httpcomponents/httpclient-cache/4.5.13/httpclient-cache-4.5.13.jar from central
Retrieving org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar from central
Retrieving org/apache/httpcomponents/httpasyncclient/4.1.4/httpasyncclient-4.1.4.jar from central
Retrieving org/apache/httpcomponents/httpcore-nio/4.4.10/httpcore-nio-4.4.10.jar from central
Retrieving org/apache/httpcomponents/httpmime/4.5.13/httpmime-4.5.13.jar from central
Retrieving commons-codec/commons-codec/1.15/commons-codec-1.15.jar from central
Retrieving commons-io/commons-io/2.8.0/commons-io-2.8.0.jar from central
Retrieving clj-http/clj-http/3.12.3/clj-http-3.12.3.jar from clojars

外部リポジトリについて

  • 検索同様、leinはMavenCentral、Clojarから依存ライブラリを取得する。
  • MavenCentralはご存知の通り、Javaの主なライブラリが置いてある。
  • Clojarは主にClojurコミュニティが集中してライブラリを配置している。
  • その他のリポジトリを参照する場合は、project.cljの :repositories で設定する。
  • sample.project.clj が参考になる。

Clojure Leiningen その2

ディレクトリレイアウト

ファイル名から名前空間へのマッピング規則

src/
└── temp/
    └── core.clj

1 directory, 1 file
  • これは単一セグメント(上記の場合、tempの直下)の名前空間は推奨されないからであると。
  • 情報のソースを辿ってみましたが、どうやらJavaとの相互運用性に関する問題があり、単一セグメントの名前空間を使用するとJavaのデフォルトパッケージにClojureのコードが置かれてしまうので、やめましょう。という慣習のようです。
  • その慣習に則って、leinではプロジェクト名の後ろにcoreをつけた名前空間を作成する、ってことかと。
  • ちなみに、core.cljの中身はこんな感じ。
(ns temp.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))
  • temp.core という名前空間になっています。
  • また、Clojure名前空間- は実際のパスの場合 _ と書き換える必要があります。
  • これもまたJavaの影響を受けていて、Javaの場合、パッケージ名とクラス名でダッシュが使えないから、とのこと。

Javaが識別子、特にパッケージとクラス名でダッシュを禁止しているという事実によるものです。 https://codeberg.org/leiningen/leiningen/src/branch/stable/doc/TUTORIAL.md#filename-to-namespace-mapping-convention

Clojure Leiningen その1

Clojureを書くにあたって、Leiningenを調べて、触って、みようと思いまして。

https://leiningen.org/

  • DeepL先生に和訳をお願いしたところ、ライニンゲンと出てきました。どういう意味なんだろう?読み方は合っているのか?
  • (Leiningenはちと長いんで、leinと呼ぼうと思います)
  • ちなみに、環境はこんな感じです。
vagrant@ubuntu2204:~$ lein --version
Leiningen 2.9.10 on Java 17.0.5 OpenJDK 64-Bit Server VM

もしあなたがJavaの世界から来たのなら、Leiningenは「MavenとAntを苦もなく結びつけたもの」だと考えることができるでしょう。RubyPythonの人たちにとっては、LeiningenはRubyGems/Bundler/Rakeとpip/Fabricを1つのツールにまとめたものです。 https://codeberg.org/leiningen/leiningen/src/branch/stable/doc/TUTORIAL.md

  • ざっと見、JavaでいうMaven、Gradleあたりの立ち位置なのがわかりますね。REPLはClojure独自の機能といったところ。maven形式のpomも生成できるのか。
  • lein関係ないですが、MavenCentralにあたるものがClojarsという場所らしいですね。
  • https://clojars.org/
  • leinが依存関係を解決する場合、ここを見に行くんでしょうきっと。

メタデータはプロジェクトのルート・ディレクトリにあるproject.cljというファイルに保存され、Leiningen に以下のようなことを伝えることができます。 https://codeberg.org/leiningen/leiningen/src/branch/stable/doc/TUTORIAL.md

  • Mavenのpom.xmlに該当するものが、project.cljのようですね。
  • lein new コマンドでプロジェクトを新規生成できるようです。引数は色々とありますが、テンプレートと名前はよく使いそうです。テンプレートはどのようなプロジェクトを作成するかを指定するもので、デフォルトはライブラリに適したものになるらしいです。
  • 呼び方はこんな感じ。

lein new $TEMPLATE_NAME $PROJECT_NAME

  • 作ってみます。引数が一つの場合には、テンプレート名ではなく、プロジェクト名とみなされるようです。
vagrant@ubuntu2204:~/source$ lein new temp
Generating a project called temp based on the 'default' template.
The default template is intended for library projects, not applications.
To see other templates (app, plugin, etc), try `lein help new`.
vagrant@ubuntu2204:~/source$ tree -F -a --dirsfirst temp
temp/
├── doc/
│   └── intro.md
├── resources/
├── src/
│   └── temp/
│       └── core.clj
├── test/
│   └── temp/
│       └── core_test.clj
├── CHANGELOG.md
├── .gitignore
├── .hgignore
├── LICENSE
├── project.clj
└── README.md
  • デフォルトのproject.cljです。
(defproject temp "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :repl-options {:init-ns temp.core})

clojureからJavaを使う

clojureJavaの呼び出し方を調べたのでそのメモです。

使用するJavaクラスをインポートするには、 import フォームを使います。 https://clojuredocs.org/clojure.core/import

user=> (import '(java.io File))
java.io.File

パッケージ名とクラス名の間にスペースが必要になることに注意が必要かと。また、複数のクラスを列挙することもできます。

user=> (import '(java.io File FileInputStream FileOutputStream))
java.io.FileOutputStream

new を使うことで、クラスをインスタンス化できます。 https://clojuredocs.org/clojure.core/new

(def f (new File "."))
#'user/f

. でメソッドを呼び出します。 https://clojuredocs.org/clojure.core/_.

user=> (. f exists)
true
user=> (. f getAbsoluteFile)
#object[java.io.File 0x7dd3f72c "/home/vagrant/."]

クラスメソッドも同様の手段で呼び出すことができます。

user=> (import '(java.time LocalDate))
java.time.LocalDate
user=> (. LocalDate now)
#object[java.time.LocalDate 0x6b1e1660 "2022-11-06"]

少し寄り道して今日の日付から10日後の日付を返す関数を定義してみます。

user=> (defn days-add-from-now [days]
  #_=>   (-> (. LocalDate now)
  #_=>       (. plusDays days)))
#'user/days-add-from-now
user=> (days-add-from-now 10)
#object[java.time.LocalDate 0x4eccadd1 "2022-11-16"]

上記の関数は次の関数と等価のはず。

user=> (defn days-add-from-now [days]
  #_=>   (. (. LocalDate now) plusDays days))
#'user/days-add-from-now
user=> (days-add-from-now 10)
#object[java.time.LocalDate 0x1880b005 "2022-11-16"]