Skip to content

Commit 96458fa

Browse files
authored
Merge pull request #163 from haskell-jp/asterius
記事の追加: strip-ansi-escapeというパッケージをリリースしました
2 parents ac688cd + a8ec637 commit 96458fa

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
title: strip-ansi-escapeというパッケージをリリースしました
3+
headingBackgroundImage: ../../img/background.png
4+
headingDivClass: post-heading
5+
author: Yuji Yamamoto
6+
postedBy: <a href="http://the.igreque.info/">Yuji Yamamoto(@igrep)</a>
7+
date: July 8, 2019
8+
tags:
9+
...
10+
---
11+
12+
現職でHaskellを仕事で書き始めるようになってからというもの、度々小さなパッケージをリリースするようになりました。
13+
敢えてパッケージにするほどのものでもなさそうなぐらい小さなものが多いですが、もし再利用したくなったらな、という気持ちで書いております。
14+
15+
# なに作ったか
16+
17+
[strip-ansi-escape](http://hackage.haskell.org/package/strip-ansi-escape)というパッケージです。
18+
今回もメインの処理は100行にも満たないような小さなもので、また用途もニッチです。
19+
具体的には、名前のとおり[ANSIエスケープコード](https://en.wikipedia.org/wiki/ANSI_escape_code)を文字列から取り除く、ただそれだけです。
20+
使い方も極めてシンプル:
21+
22+
```haskell
23+
ghci> import Data.String.AnsiEscapeCodes.Strip.Text
24+
25+
-- 現状Text型向けにしか作っていないため、OverloadedStringsを有効にした方が使いやすい
26+
ghci> :set -XOverloadedStrings
27+
ghci> import Data.Text
28+
29+
-- 出力すると下線付きで "hello" と表示されるANSIエスケープコード付きの文字列
30+
ghci> "\x001B[4mhello\x001B[0m"
31+
"\ESC[4mhello\ESC[0m"
32+
33+
ghci> stripAnsiEscapeCodes "\x001B[4mhello\x001B[0m"
34+
"hello"
35+
```
36+
37+
# なぜ作ったか
38+
39+
通常我々がANSIエスケープコードを扱うときは、**ユーザーのために**端末に文字列を分かりやすく表示したいときで、それをプログラムで再利用することは想定していません。
40+
そのためANSIエスケープコードを出力できるアプリケーションは、大抵の場合出力しないよう設定できる<small>(あるいは、出力先がttyでないことを検出して出力しない)</small>ようになっています。
41+
なので、プログラムがANSIエスケープコードの混ざった文字列を扱わざるを得ない、という事態は、何かがおかしい事態だと言えるでしょう。
42+
43+
一体どういう事態なのかというと、それは私がずっと開発中の、対話的Haskell入門コンテンツ --- [「失敗しながら学ぶHaskell入門」](https://github.com/haskell-jp/makeMistakesToLearnHaskell) --- で出遭った事態でした。
44+
「失敗しながら学ぶHaskell入門」(以下、英語名を略して「mmlh」と呼びます)では、ユーザーが書いたHaskellのソースコードを受け取って、GHCにコンパイルさせることで、型エラーなどのエラーメッセージを取得しています。
45+
当初からmmlhはそれを簡単にパースしてユーザーへのヒントを出すのに使ったり、ユーザーにそのまま表示したりするのに使うため、`-fdiagnostics-color=always`というオプションをGHCに渡していました。
46+
これは、エラーメッセージに色を着けるようになったGHC 8.2から導入されたオプションで、「エラーメッセージに必ず<small>(ANSIエスケープコードを使って)</small>色を着ける」というものです。
47+
GHCが出すエラーメッセージを「簡単にパース」しつつ「ユーザーにそのまま表示」する、という2つの要件を満たすためには、このオプションを利用して、強制的にエラーメッセージに色を着ける必要がありました。
48+
49+
さらに最近、GHCが出したエラーメッセージをファイルに保存して、[GitHubで閲覧できるようにする](https://github.com/haskell-jp/makeMistakesToLearnHaskell/issues/101)<small>(正確には、閲覧して各行にコメントできるようにする)</small>、という機能も追加した結果、ANSIエスケープコードを取り除かざるを得なくなってしまったのです。
50+
というのも、`-fdiagnostics-color=always`を有効にしている限り、GHCは必ずANSIエスケープコードをエラーメッセージに混ぜるので、ファイルに保存してGitHub上で表示する際、下記のように余計な文字として混ざってしまい、エラーメッセージが読みづらくなってしまうためです。
51+
52+
```
53+
�[;1m16.hs:19:18: �[;1m�[31merror:�[0m�[0m�[;1m�[0m�[0m�[;1m
54+
• No instance for (Num ([Char], String))
55+
arising from a use of ‘countWords’
56+
• In the expression: countWords (concat wordsList)
57+
In an equation for ‘countMap’:
58+
countMap = countWords (concat wordsList)
59+
In the expression:
60+
do paths <- getArgs
61+
wordsList <- for paths scrapeWords
62+
let countMap = countWords (concat wordsList)
63+
for_ (toList countMap) catCount�[0m�[0m
64+
�[;1m�[34m |�[0m�[0m
65+
�[;1m�[34m19 |�[0m�[0m let countMap = �[;1m�[31mcountWords (concat wordsList)�[0m�[0m
66+
�[;1m�[34m |�[0m�[0m�[;1m�[31m ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^�[0m�[0m
67+
�[0m�[0m�[0m
68+
```
69+
70+
`-fdiagnostics-color=always`を有効にしなければこんな問題は起こらないのですが、そうすると今度はユーザーにエラーメッセージを表示させる際、色が着かなくなってしまいます。
71+
せっかくGHC 8.2以降を使っているのに色つきのエラーメッセージが見られないのは残念ですよね。
72+
GHCを2回実行することで、ユーザーに表示する用のエラーメッセージとファイルに保存する用のエラーメッセージを分けることもできますが、それでは効率が悪いでしょうし。
73+
74+
そんなわけで、GHCが出力するエラーメッセージを**ユーザーに端末上で表示する用途と、ANSIエスケープコードを解釈しない箇所で表示する用途**、両方に使用したくなったため、今回敢えてANSIエスケープコードを取り除くライブラリーを作りました。
75+
もし他に同じような事態に出遭った方がいらっしゃいましたら、試してみてください🙏
76+
77+
# 最近のmmlh
78+
79+
ついでにここ数ヶ月弊社でやっている、mmlhを使った社内勉強会のお話も書こうかと思いましたが、やっぱり社内でのことなんで、[会社のブログ](https://eng-blog.iij.ad.jp/)に書くことにします。
80+
多分今週中には上げますので乞うご期待!

0 commit comments

Comments
 (0)