狐の嫁入りっていいよね

理系と芸術系になりそなった文系卒、コンピュータグラフィックスを学ぶ

素数判定のアルゴリズムをC#で書いた

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造

螺旋本 18.1 素数判定より、理解したことと書いたコードを載せる。

ぐぬぬ...」をたくさん繰り返したが、その都度考えて「あーそうかそういうことか!」ってなって楽しかった。

素数 is 何

「自分自身と1以外で割り切れない自然数のこと」を素数(Prime Number)という。

当たり前じゃんと思う方も要るかもしれないが、この定義を「自分自身と1以外で割り切れない」と「自然数」の2つに分解して考えてみる。

文系卒なのでどうかお手柔らかに。


①「自分自身と1以外で割り切れない」とは。

ここで言う「数」とは自然数を指す(②で後述)

たとえば、5は素数であるが、これは5という数は5自身と1以外で割り切れないため。

そして、ある数を「割り切れる数」のことを約数という。

また、約数(Common Number)が「自分自身」と「1」の2つであるとき、それを素数という。

例を上げる。

3は素数。なぜなら、3は1と3でしか割り切れず、約数が2つであるから。

対して、4は素数ではない。なぜなら、4は1と2と4で割り切れ、約数が3つであるから。

また、1は約数ではない。なぜなら、1は1でしか割り切れず、約数が一つであるから。

②「自然数とは。

自然数(Natural Number)とは何か。

自然数については以下のマインドマップに整理した。このほうがわかりやすいとおもわれる。

実数 所有者: okitsune concon

要するに自然数は正の整数で、例えば「1,2,3,4,5....」を指す。


まとめると、

「自分自身と1以外で割り切れない自然数のこと」を素数(Prime Number)

これは換言すると、

①割り切れる数(約数)が「自分自身」と「1」の2つであり、② 1を除く正の整数 素数である。

さて、この理解をもとに、素数かどうかを判定するコードを作る。


素数判定の実装 その1

愚直に書いたら以下のようになる。

        public static bool IsPrime(int x)
        {
            if (x <= 1) return false; // 1以下は素数ではない

            //自分自身と1以外で割れるかどうか
            int y = x - 1;
            while(y > 1)
            {
                if (x % y == 0) return false;
                y--;
            }

            return true;
        }

これでも動くが、計算量がO(N)であり、遅い。

実際、Time Limit Exceededで処理時間が遅くて時間超過ですという判定を食らっている。

#3404197 Solution for ALDS1_1_C

改善の必要がある。


素数の実装 その2

ここで、合成数xはp<=√xを満たす素因子pを持つ」という性質を利用して高速に処理できるようにする。

は???「合成数xはp<=√xを満たす素因子pを持つ」???

文系にわかるように話して???

となったので、腰を据えて考えた。

合成数素数ではない数のことを指す。

素因子とは「その数の約数であり、かつ、素数である」数を指す。

数学において、ある自然数素因数(そいんすう、: prime factor)とは、その約数になる素数のことである。ある数の素因数を求めてその積の形で表すことを素因数分解という。例えば 60 は 22×3×5 と素因数分解されるので 60 の相異なる素因数は 2, 3, 5 の3つである。

素因数 Wikipedia

合成数 N は 1 より大きく √N 以下の約数をもちます


まとめると、

合成数素数ではない数のことを指す。

素因子とは「その数を割り切れる数であり、かつ、素数である」数を指す。つまり、「その数の約数であり、かつ、素数」のことをさす。

なので、合成数xはp<=√xを満たす素因子pを持つ」という文言は、素数でない数xはp<=√xを満たす、xの約数でありかつ素数であるpが存在する」ということを指す。

①割り切れる数(約数)が「自分自身」と「1」の2つであり、② 1を除く正の整数 **が素数である。

と上で述べたが、ここから「1を除く正の整数」という条件がわかるので、pの取りうる範囲は1 < p <= √x になる。

なので、

1 < p <= √x の範囲内で、割り切れる数が存在すれば、その数xは素数ではない

1 < p <= √x の範囲内で、割り切れる数が存在しなければ、その数xは素数である

ということになる。


例えば、

12が素数かどうか判定するときには、1 < p <= √12 (= 3.464...) の中の整数 2と3で12が割れるかどうかを調べればいい。

12はもちろんpで割り切れるので、これは素数ではない。

対して、

13が素数かどうか判定するときには、1 < p <= √13 (= 3.6055...) の中の整数 2と3で13が割れるかどうかを調べればいい。

13はpでは割り切れないので、13は素数である。

これをコードに載せる。

        public static bool IsPrimeB(int x)
        {
            if (x <= 1) return false;
            
            // Composite Number ==  not Prime Number
            // xは 1 < i <= root(x) で割り切れるかだけを調べる
            double r = Math.Sqrt(x);
            for (int i = 2; i <= r; i++)
            {
                if (x % i == 0) return false;
            }

            return true;
        }

計算量はO(√x)なので、O(N)よりも相当速くなる。なので、処理速度で怒られることがなくなった。

Solution for ALDS1_1_C


素数の実装 その3

その2まででも良いのだが、エラトステネスのふるいというアルゴリズムを使ってNまでの素数を列挙するアルゴリズムを実装する。

2からNまでの整数を列挙する。

最小値である、2を残して、2の倍数を削除する

残った最小値である、3を残して、3の倍数を削除する。

残った最小値である、5を残して、5の倍数を削除する。

...ということを繰り返すアルゴリズム


        public static void SieveOfEratosthenes(int n)
        {
            bool[] isPrime = new bool[n+1];
            for (int i = 0; i < n; i++)
            {
                isPrime[i] = true;
            }

            isPrime[0] = false;
            isPrime[1] = false;

            for (int i = 2; i < Math.Sqrt(n); i++)
            {
                int j;
                if (isPrime[i])
                {
                    j = i + i;
                    while (j <= n)
                    {
                        isPrime[j] = false;
                        j = j + i;
                    }
                }
            }


            for (int i = 0; i < isPrime.Length; i++)
            {
                if (isPrime[i])    
                {
                    WriteLine(i);
                }
            }
        }
    public class ALDS1_1_C
    {
        public static void Main(string[] args)
        {
            int N = int.Parse(ReadLine());
            SieveOfEratosthenes(N);
        }
    }
// N = 120
120
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
101
103
107
109
113

あるアカウントのGoogle Keepを他のアカウントに全てコピーした

TL;DR

Google Keepの中身を他のアカウントに全コピーするPythonスクリプトを書いた。



経緯

今日、デザイナーさん達が隣でこんな話をしていた。

「諸事情でgoogleアカウントが使えなくなってしまうから、Google Keepで書いたメモ、他の自分のアカウントに移さないといけないんだよねえ」

「そうそう、あのコミュニティのGoogleアカウントのGoogle Keepでひたすら二年間メモしまくってきてたくさんあるから移すのめんどくさいなあ」

「これ一々手動でコピーしなきゃいけないよえ、めんどくさいねえ」

...

...

ぼく「...それ自動化できません?」

ここに言い出しっぺの法則が発動した。

「努力は楽をするためにある」が私の信条なので、私はワンパンでコピーができなければ気がすまなかった。

要するに極度のめんどくさがりなのである。

なんの生産性もない悲しい時間なんてなくなってしまえばいい。

あわよくば自動化の見返りとしてセブンイレブンのホットコーヒー(L)をおごってくれれば更に良い。

そんなわけで「極度のめんどくさがり VS ルーティンワーク」が始まった。



調査

公式APIがなかったので非公式のAPIを使うことにした。


最初はGoogle APIをコマンドで叩けばワンパンだろ。。。と思っていたが、調べたらそもそもGoogle Keepには公式のGoogle APIがなかった。

Google APIとはコマンドを叩けばグーグルのサービスをいろいろごにょごにょできるツール。例えば、ダウンロードとかアップロードとかをプログラミングで実行してくれる。何がいいかって、やりようによってはcronなどと併用すればアップロードやダウンなどを自動化できる。

「それ自動化できません?」と言った手前、「できませんでした」と言ったらフルボッコにされそうだったので頑張って代替案を探した。


二種類の代替案が見つかった。

Google TakeoutというGoogle公式バックアップサービス

まず、Google Takeoutという公式のバックアップサービスを見つけた。このサービス、Google Keepもバックアップできるが、HTMLファイルをzip形式なりtar形式なりに固めて出力される。ただ、Google Keepにそもそもインポート機能がない。 インポート機能自体はTake outしないでほしいなあ...。 なのでアカウントを跨いだデータ移行にはGoogle Takeoutは使えない。

②非公式のGoogle Keep API

次に、非公式ではあるものの、GithubGoogle KeepのAPIが転がっていた。なのでありがたく使わせていただくことにした。

kiwiz gkeepapi

gkeepapi's Documentation



実装

環境

Ubuntu 18.04LTS
Python3.6
pip install  gkeepapi

Pythonスクリプトはこんな感じになった。

import gkeepapi

# your google account to EXPORT google keep notes
export_account = gkeepapi.Keep()
e = export_account.login('export_account@gmail.com', 'your_password_xxxx')

# your google account to IMPORT google keep notes
import_account = gkeepapi.Keep()
i = import_account.login('import_account@gmail.com', 'your_password_yyyy')

# Fetch your all notes from export_account
notes = export_account.all()

# Copy all notes for your import account
for note in notes:
    if note.title == None:
        title = ""

    if note.text == None:
        text = ""

    print(note)
    print("---------------------------------")
    title = note.title
    text = note.text

    import_account.createNote(title=title, text=text)

# Save imports 
import_account.sync()

このスクリプトを実行すれば、一発であるGoogleアカウント内のGoogle Keepの全データをほかのGoogleアカウントのGoogle Keepにコピーできる。

自分の日常的に使っているGoogleアカウントからGoogleサブアカウントにコピーできることを確認したので普通に動くはず。 リストも画像も全てコピーされていた。

めでたし。

vimでyankしたテキストをクリップボードに渡す

f:id:lipton_lemontea:20190114113208p:plain

vimでテキストをコピー(=ヤンク)するときに、デフォルトではシステムの方のクリップボードと連携されない。

なので、vimでヤンクしたものをCtrl+Vで貼り付けようとしても貼り付けられない。

環境

# Ubuntu 18.04 LTS
# vim-gtk

$ uname -a
Linux hoge 4.15.0-43-generic #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

$ cat /etc/os-release | grep VERSION
VERSION="18.04.1 LTS (Bionic Beaver)"

$ vim --version | grep -e Huge -e Small -e Normal
Huge 版 with GTK2 GUI.  機能の一覧 有効(+)/無効(-)

参考にしたサイト

How to make vim paste from (and copy to) system's clipboard?

Linux vimでヤンクした結果をC-vで貼り付けたい(クリップボードの共有)

vimの状態が-clipboard となっていたらクリップボードに対応していないということなので、+clipboard にしてやる必要がある

:vim --version | grep clip
続けるにはENTERを押すかコマンドを入力してください
[最後の変更が保存されていません]
# 非対応
-clipboard         +jumplist          +persistent_undo   +virtualedit
-ebcdic            -mouseshape        +statusline        -xterm_clipboard
# clipboardに対応したvimをインストールする
$ sudo apt-get install vim-gtk -y
:vim --version | grep clip
続けるにはENTERを押すかコマンドを入力してください
[最後の変更が保存されていません]
# 対応するようになった
+clipboard         +jumplist          +persistent_undo   +virtualedit
-ebcdic            +mouseshape        +statusline        +xterm_clipboard

.vimrcファイルに以下のように追記をする

set clipboard=unnamedplus

これでvimのヤンクを実行したらクリップボードに貼り付けられるようになる。

めでたし。

iPadとBlinkでAWSとGCPにSSH接続した

f:id:lipton_lemontea:20190113202505p:plain

BlinkっていうアプリがiOSにある。

Blink Shell: iOSでmoshを使う

自分のマシンからネットワークを通じてどこかにアクセスする場合、やりとりを盗み見される可能性がある。

素の状態だと情報が筒抜けになってしまうので、盗み見を防ぐために、SSH(Secure SHell)と呼ばれる暗号化手段がある。それをiOSで実現できるアプリの一つとして、Blinkというアプリが挙げられる。

今回はそのBlinkを使って、AWSGCPで作成したLinuxインスタンスにアクセスできるようにした。

AWS(Amazon Web Service)とGCP(Google Cloud Platform)はamazongoogleがそれぞれ提供しているサーバーなどを簡単に作れる開発者向けサイトのこと。

GCPインスタンスSSHでログインしたところ。長くなったので、GCPの方は別の記事にする。

f:id:lipton_lemontea:20190113202615p:plain

AWSインスタンスSSHでログインしたところ

f:id:lipton_lemontea:20190113202621p:plain

簡単にいうとiPadからLinuxサーバーを操作してる感じ。



ハマったところ



AWSで接続する

AWSでまずLinuxインスタンスを作成する。

AWSにログインしてEC2をクリック

f:id:lipton_lemontea:20190113202512p:plain


インスタンスの作成をクリック

f:id:lipton_lemontea:20190113202518p:plain


Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type - ami-02fd0b06f06d93dfc を選択

f:id:lipton_lemontea:20190113204739p:plain


インスタンスタイプは特に指定せずに(無料枠のまま)確認と作成をクリック

f:id:lipton_lemontea:20190113204912p:plain


作成をクリック

f:id:lipton_lemontea:20190113205020p:plain


新しいキーペアの作成 > キーペア名を入力(ここではblinkと入力した) その後、キーペアのダウンロードをクリックして、インスタンスの作成をクリック。

f:id:lipton_lemontea:20190113205237p:plain

そうすると[キーベア名].pemという名前(ここではblink.pem)で公開鍵暗号方式秘密鍵が作られる。公開鍵はインスタンスに自動で登録されるっぽい。

このblink.pemを何らかの手段でiPadに送る。(私はGoogle Driveにアップロードしたが手段としては人に勧められなさそう)

iPadで「ファイルに保存」

f:id:lipton_lemontea:20190113202640p:plain


このiPad内 > 「Blinkフォルダ」に追加

f:id:lipton_lemontea:20190113202646p:plain


ブラウザに戻って、インスタンスの表示をクリック

f:id:lipton_lemontea:20190113205533p:plain


パブリックDNS(赤線の部分)をコピペする

f:id:lipton_lemontea:20190113202622p:plain

これで接続する準備ができた。

以下のコマンドで接続できるようになる。

ssh -i blink.pem ec2-user@[パブリックDNS]

次にiPadのBlinkからこのインスタンスSSH接続をする。

# 何故かblink.pemのはずがblink.cerになっているので修正
blink> ls
blink.cer       iCloud
blink> mv blink.cer blink.pem

# linuxインスタンスにssh接続
blink> ssh -i blink.pem ec2-user@[your public DNS]
xxxxx key fingerprint is SHA256:xxxxxxxxxxxx
The server is unknown.
Do you trust the host key? (yes/no):yes
This new key will be written on disk for further usage.
Do you agree? (yes/no):

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
10 package(s) needed for security, out of 12 available
Run "sudo yum update" to apply all updates.
[ec2-user@my_aws ~]$ 

これでiPadLinuxに引きこもれるようになった。ひゃっほい。

WindowsでAnkiのフォルダを引っ越した

みんな大好きAnki

f:id:lipton_lemontea:20181128194247j:plain
Anki

最近Ankiのメディアディレクトリが大きくなってきて、容量が100GBのCドライブ圧迫している(SSD)。

対して、DドライブがHHDで1TB近く空きがあり、ほとんど使っていないのでAnkiのデータを引っ越すことにした。

ディレクトリの引っ越しをする

プラグインやバックアップの情報や画像や音声、動画などはWindowsではデフォルトで以下のディレクトリに入っている。

C:\Users\xxx\AppData\Roaming\Anki2

なのでgit bashで中身を確認した。

 MINGW64 ~/Appdata/Roaming/Anki2
$ ls -l
total 62
drwxr-xr-x 1 xxx 197121     0 11月 11 22:32 addons/
drwxr-xr-x 1 xxx 197121     0 11月 11 14:01 addons21/
-rw-r--r-- 1 xxx 197121  1338 11月 10 15:26 crash.log
-rw-r--r-- 1 xxx 197121     4 11月 10 15:26 gldriver
-rw-r--r-- 1 xxx 197121 21504 11月 28 18:45 prefs.db
-rw-r--r-- 1 xxx 197121 21504 11月 11 22:31 prefs21.db
-rw-r--r-- 1 xxx 197121   273 9月  30  2017 README.txt
drwxr-xr-x 1 xxx 197121     0 11月 28 18:45 ME/

はい。(ただディレクトリの中身を見ているだけ)


それで、新しくD:\Ankiディレクトリを作成して上記ディレクトリをコピーしてやる。

$ pwd
/d/Anki

/d/Anki
$ ls -l
total 62
drwxr-xr-x 1 xxx 197121     0 11月 28 18:46 addons/
drwxr-xr-x 1 xxx 197121     0 11月 28 18:46 addons21/
-rw-r--r-- 1 xxx 197121  1338 11月 10 15:26 crash.log
-rw-r--r-- 1 xxx 197121     4 11月 10 15:26 gldriver
-rw-r--r-- 1 xxx 197121 21504 11月 28 18:45 prefs.db
-rw-r--r-- 1 xxx 197121 21504 11月 11 22:31 prefs21.db
-rw-r--r-- 1 xxx 197121   273 9月  30  2017 README.txt
drwxr-xr-x 1 xxx 197121     0 11月 28 19:03 ME/

...はい。

この引越し作業はもちろんエクスプローラーでドラッグアンドドロップもしくはコピペでやってもいい。

これで /d/Anki以下に画像とかプラグインの情報が諸々入っている~/Appdata/Roaming/Anki2の内容が移った。


引っ越しの通知をAnkiに届ける

ディレクトリの引越し作業は終わった。

次に、Ankiにフォルダを変えましたよ~ってどう教えるのかということが問題になってくる。

~/Appdata/Roaming/Anki2/README.txtの中身を見てみると以下のようなことが書かれている。

このフォルダーは、全ての Anki データを保存する唯一の場所です。これによってバックアップが簡単になります。別の場所を設定するには次の情報をご覧ください: http://ankisrs.net/docs/manual.html#startupopts

...

そうですか。

...

というわけで上記アドレスのドキュメントを見てみた。

Anki Manual

If you always want to use a custom folder location, you can modify your shortcut to Anki. On Windows, right-click on the shortcut, choose Properties, select the Shortcut tab, and add "-b \path\to\data\folder" after the path to the program, which should leave you with something like

"C:\Program Files\Anki\anki.exe" -b "C:\AnkiDataFolder"


訳:以降ずっと任意のディレクトリを指定してAnkiを起動したいときはAnkiのショートカットのプロパティから設定してね

Ankiのショートカットを右クリック>プロパティ>ショートカットタブを選んで>リンク先に以下のように指定してあげてね

"C:\Program Files\Anki\anki.exe" -b "任意のディレクトリ"


上記に書いてあることをそのまま実行するだけなのだが、 英語だと若干ややこしいので、そのときにやったことのスクリーンショットを添付する。

before/afterで以下の二枚のスクショを撮った。下記赤線の

"C:\Program Files (x86)\Anki\anki.exe"

"C:\Program Files (x86)\Anki\anki.exe" -b "D:\Anki"

に変更した。

before

f:id:lipton_lemontea:20181128185948j:plain

このようになる

after

f:id:lipton_lemontea:20181128185846j:plain


その後OKを押すと

管理者権限でやれって怒られるので「続行」をクリック

f:id:lipton_lemontea:20181128190141p:plain


これでAnkiに、ディレクトリの引っ越しをしましたよ!という通知が行える。

Ankiに引っ越し通知が行ったかどうかの確認

最後に、「本当にディレクトリの引っ越しができたのか」の確認をしてやる必要がある。

Ankiを起動して、

ツール>アドオン>アドオンフォルダを開く

なりをクリックする。(同期に若干時間がかかる)

f:id:lipton_lemontea:20181128192349j:plain

先程指定したディレクトリの中にあるD:\Anki\addonsが表示されたのでAnkiにディレクトリの引っ越しの通知が行ったようだ。

(ただしショートカットのアイコンが複数ある場合はその都度修正する必要がある。)

これで逼迫しているCドライブの容量を減らすことができた。

めでたし。

Riderの設定をGithubで完全同期できるようにした

f:id:lipton_lemontea:20181113145107p:plain

TL; DR

Jetbrainsエディタの設定をGithubと同期できるSetting Repositoryというプラグインがある。しかしこのSetting Repository はデフォルトでは中途半端なGithub同期しかしないので、Jetbrains Riderの設定を完全に同期できるようにした。

環境

Windows10
Jetbrains Rider 2018.2

経緯

下記のエントリーのようにJetbrains Riderが起動しなくなった。

kitsune-no-yomeiri.hatenadiary.jp

一応この問題は解決したのだが、その解決方法が「今までにいろいろ作ってきた設定やプラグインをすべて消して再インストール」することだった。

この解決方法はいままで作ってきた『数年継ぎ足した秘伝のタレ』を捨てるようなもので、泣く泣く実行せざるを得なかった。だって設定とかキーマッピングとかって自分が使いやすいようにちまちま育てていくもんなんですよ...。それ全部削除ってめっちゃつらい...。

なので、同じことが起こらないように『数年継ぎ足した秘伝のタレ』をGithubにpushして、Riderがぶっ壊れても設定を復元できるような仕組みを模索できたのでエントリーとして残す。


設定をGithubと同期するJetbrains謹製プラグインは一応はある

Setting Repositoryというプラグイン

Supports sharing settings between installations of IntelliJ Platform based products used by the same developer (or team) on different computers.

Synchronization is performed automatically after successful completion of "Update Project" or "Push" actions. Also you can do sync using VCS -> Sync Settings.


(訳: 開発者が複数のPC間でもしくはチーム間でJetbrainsのエディタを使うときに役に立つよ!GithubにPushするなりしたら設定が同期されるよ!)

ということで試したが、結論から言うとこれだけでは設定は部分的にしか同期されない。後述するが、完全に同期するには自作の.DotSettingsファイルをここに追加して、ここの.DotSettingsファイルを使用するよう設定する必要がある。

(以下の記事を参考にしてSetting Repositoryの設定ができる。)

qiita.com

マクロやキーマッピングやカラースキームは同期されるが、Live Templateやコード規則は同期されない。特にLive Templateは自作できたりするし、使用頻度もかなり高い。カスタムしたLive Templateが同期しないのはちょっといただけない。


Settings repositoryの詳細を調べた

ということで同期されているディレクトリの詳細をgit bashで見てみた。

  • Setting RepositoryがGithubと同期するのはデフォルトではWindows10であれば~/.Rider2018.2/config/settingsRepository/repository以下のディレクトリのようだ。
$ pwd
/c/Users/xxx/.Rider2018.2/config/settingsRepository/repository
# ここの内部構造を見てみる

~/.Rider2018.2/config/settingsRepository/repository (master)
$ pwd;find . | sort | sed '1d;s/^\x;s/\/\([^/]*\)$/|--\1/;s/\/[^/|]*/|  /g'
/c/Users/xxx/.Rider2018.2/config/settingsRepository/repository
|--.git
| 
|--_windows
|  |--git.xml
|  |--keymap.xml
|  |--laf.xml
|--codestyles
|  |--Default.xml
|--colors.scheme.xml
|--debugger.xml
|--editor.codeinsight.xml
|--ide.general.xml
|--JetbrainsRider.DotSettings //自分で作成した.DotSettingsファイル(後述)
|--keymaps
|  |--Default copy.xml
|--macros.xml
|--terminal.xml
|--ui.lnf.xml
|--vsts_settings.xml

このディレクトリを見る限り、デバッガ、マクロ、ターミナル、キーマップ、カラースキーマらへんの設定はGithubと同期してるっぽい。

他のJetbrainsのエディタでは~/.<product ><version>/config以下にLive Templateのディレクトリ(templates)が作られるようだが、Jetbrains Riderでは作られていない模様。

参考: IntelliJ IDEA 2018.2では%HOMEPATH%\.<product><version>\config\templatesディレクトリが自動で作られる www.jetbrains.com

  • 他のJetbrainsのエディタではtemplatesディレクトリが作られる。
  • 対して、Jetbrains Riderではtemplatesディレクトリは作られていない。

...

ふむ。

じゃあ、Jetbrains RiderのLive Templateの情報はどこに保存されているのだろう


RiderのLive Templateの情報は.DotSettingsファイルに保存されている

もろもろ調べていたら C:\Users\xxx\.Rider2018.2\config\resharper-host\GlobalSettingsStorage.DotSettings というファイルの中にLiveTemplatesという文言があった。

# C:\Users\xxx\.Rider2018.2\config\resharper-host\GlobalSettingsStorage.DotSettings

    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=2A7799E642B6AF499EA6D709A1546A87/@KeyIndexDefined">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=2A7799E642B6AF499EA6D709A1546A87/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=2A7799E642B6AF499EA6D709A1546A87/Reformat/@EntryValue">True</s:Boolean>

このファイルが怪しい...。でも.DotSettingsファイルってなんだろう...。


.DotSettings is 何

JetBrains Rider 2018.2 Help Layer-Based Settings

Reshaper 2018.2 help Managing and Sharing ReSharper Settings

Out of the box, ReSharper comes with the default set of preferences, which are based on conventions and best practices in the .NET world. These default settings are hard-coded in the product and you can always reset to the defaults if necessary. If you change any settings, your change is saved in a settings layer and ReSharper applies it overriding the corresponding default setting. Initially, ReSharper suggests three layers, in which you can save your preferences: This computer, Solution team-shared, and Solution personal.

ここらへんを見てみると.DotSettingsは.NET界隈の設定ファイルのようで、レイヤーで設定を塗り替えて管理することができるがみたいだ。この引用はReshaperのドキュメントから引っ張ってきた。.NETの設定管理方法(?)をReshaperが採用しており、RiderはReshaperをもとに開発されている。だからRiderには~/.Rider2018.2/config/以下にtemplates/がなく、いろんな設定をReshaperと同様に.DotSettingsで管理しているのだと思われる。

Jetbrains Riderでは複数の.DotSettingsが存在しており、基本的に3つのレイヤーで分けられている。そしてレイヤーの優先順位でどう設定をされるかが決まるようだ。


.DotSettingsの3つのレイヤー構成

デフォルトで三種類の.DotSettingsファイルが存在する。

<SolutionName>.sln.DotSettings.user

Solutionファイルの個人の設定を決める

<SolutionName>.sln.DotSettings

Solutionファイルのチーム間設定を決める

~\.Rider2018.2\config\resharper-host\GlobalSettingsStorage.DotSettings

今触っているPCのグローバルな設定を決める.DotSettings

(上記3つのレイヤー以外にも追加できる)


.DotSettingsの適用順位

まず、 ベースとして③のGlobalSettingsStorage.DotSettingsの設定が適用される

次にチームでの設定として② <SolutionName>.sln.DotSettingsで上書きされ、

最後に①の <SolutionName>.sln.DotSettings.user の設定で塗り替えられる

なので、自分だけの設定を割当てたいなら

① Solutionファイルの個人の設定を決める<SolutionName>.DotSettings.userに任意の.DotSettingsを指定してやればいい。


この.DotSettingsをGithubで同期できればLive Templateは保存される。

  • Setting RepositoryがGithubと同期するディレクトリは~/.Rider2018.2/config/settingsRepository/repository以下であり、(Windows10)

また、

  • 自分だけの設定を割当てたいなら上記①の<SolutionName>.DotSettings.userに任意の.DotSettingsを指定してやればいい。

なので、

~/.Rider2018.2/config/settingsRepository/repository以下にMyJetbrainsRiderSetting.DotSettingsなりを作成し、上記①の<SolutionName>.DotSettings.userMyJetbrainsRiderSetting.DotSettingsを設定してやれば

Githubに同期されるため、Githubに自作のLive Templateやその他の設定を残すことができる。

改めて先程のSetting Repositoryの中身を見てみる。

$ pwd
/c/Users/xxx/.Rider2018.2/config/settingsRepository/repository

# ここの内部構造を見てみる

~/.Rider2018.2/config/settingsRepository/repository (master)
$ pwd;find . | sort | sed '1d;s/^\x;s/\/\([^/]*\)$/|--\1/;s/\/[^/|]*/|  /g'
/c/Users/xxx/.Rider2018.2/config/settingsRepository/repository
|--.git
| 
|--_windows
|  |--git.xml
|  |--keymap.xml
|  |--laf.xml
|--codestyles
|  |--Default.xml
|--colors.scheme.xml
|--debugger.xml
|--editor.codeinsight.xml
|--ide.general.xml
|--JetbrainsRider.DotSettings //自分で作成した.DotSettingsファイル(ここに作る)
|--keymaps
|  |--Default copy.xml
|--macros.xml
|--terminal.xml
|--ui.lnf.xml
|--vsts_settings.xml

長くなってきたので、細かい手順は別の記事にします。

RiderにRainglow Color Schemesを入れたらRiderが起動しなくなった

未解決だけど、とりあえず記事にしておく。

f:id:lipton_lemontea:20181113145107p:plain

環境
Windows10
Jetbrains Rider 2018.2

Jetbrains Riderでよさげなカラースキームないかなあと思って探していて、Rainbow Color Schemesというプラグインを見つけてインストールしたらRiderがエラーを吐いて動かなくなった。

plugins.jetbrains.com

ここに

Compatible with: (... 中略 ...) Rider (... 以下略 ...)

(訳:このRainglow Color SchemesっていうプラグインはRiderでも行けるよ!)

ってかいてあるのに、再起動したら以下のエラーを吐いてRiderが起動できなくなった...。

f:id:lipton_lemontea:20181113145731p:plain

Internal error. Please report to http://jb.gg/ide/critical-startup-errors

java.lang.RuntimeException: com.intellij.ide.plugins.PluginManager$StartupAbortedException: Fatal error initializing 'com.jetbrains.rider.colorSchemes.RiderColorsSchemePatchingManager'
    at com.intellij.idea.IdeaApplication.run(IdeaApplication.java:216)
    at com.intellij.idea.IdeaApplication.lambda$initApplication$0(IdeaApplication.java:75)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:762)
    at java.awt.EventQueue.access$500(EventQueue.java:98)
    at java.awt.EventQueue$3.run(EventQueue.java:715)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:732)
    at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:361)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Caused by: com.intellij.ide.plugins.PluginManager$StartupAbortedException: Fatal error initializing 'com.jetbrains.rider.colorSchemes.RiderColorsSchemePatchingManager'
    at com.intellij.ide.plugins.PluginManager.handleComponentError(PluginManager.java:257)
    at com.intellij.openapi.components.impl.PlatformComponentManagerImpl.handleInitComponentError(PlatformComponentManagerImpl.java:43)
    at com.intellij.openapi.components.impl.ComponentManagerImpl$ComponentConfigComponentAdapter.getComponentInstance(ComponentManagerImpl.java:506)
    at com.intellij.openapi.components.impl.ComponentManagerImpl.createComponents(ComponentManagerImpl.java:107)
    at com.intellij.openapi.application.impl.ApplicationImpl.lambda$createComponents$9(ApplicationImpl.java:446)
    at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$1(CoreProgressManager.java:157)
    at com.intellij.openapi.progress.impl.CoreProgressManager.a(CoreProgressManager.java:580)
    at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:525)
    at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:85)
    at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:144)
    at com.intellij.openapi.application.impl.ApplicationImpl.createComponents(ApplicationImpl.java:453)
    at com.intellij.openapi.components.impl.ComponentManagerImpl.init(ComponentManagerImpl.java:91)
    at com.intellij.openapi.application.impl.ApplicationImpl.load(ApplicationImpl.java:405)
    at com.intellij.openapi.application.impl.ApplicationImpl.load(ApplicationImpl.java:391)
    at com.intellij.idea.IdeaApplication.run(IdeaApplication.java:209)
    ... 16 more
Caused by: java.util.ConcurrentModificationException
    at gnu.trove.THashIterator.nextIndex(THashIterator.java:83)
    at gnu.trove.TIterator.hasNext(TIterator.java:57)
    at gnu.trove.THashMap.putAll(THashMap.java:487)
    at gnu.trove.THashMap.<init>(THashMap.java:113)
    at com.intellij.openapi.editor.colors.impl.AbstractColorsScheme.copyTo(AbstractColorsScheme.java:178)
    at com.intellij.openapi.editor.colors.impl.EditorColorsSchemeImpl.copyTo(EditorColorsSchemeImpl.java:45)
    at com.jetbrains.rider.colorSchemes.ColorSchemesUtilKt.getSchemeOnlyAttributes(ColorSchemesUtil.kt:45)
    at com.jetbrains.rider.colorSchemes.RiderColorsSchemePatchingManager.b(RiderColorsSchemePatchingManager.kt:28)
    at com.jetbrains.rider.colorSchemes.RiderColorsSchemePatchingManager.initComponent(RiderColorsSchemePatchingManager.kt:21)
    at com.intellij.openapi.components.impl.ComponentManagerImpl$ComponentConfigComponentAdapter.getComponentInstance(ComponentManagerImpl.java:488)
    ... 28 more