ぷろじぇくと、みすじら。

Visual Studio CodeのシンタックスハイライトをWebページで使う

Created at:

Visual Studio Codeのエディタ部分はVisual Studio Codeがリリースされるよりも前からいろいろなところで使われていて、 最初はVisual Studio Online "Monaco"のエディタ部分として公開されていました。

それから徐々にMicrosoftの中での利用範囲が広がりTypeScriptのPlaygroundやWinJSのPlayground、 ちょっと変わったところではInternet Explorer/EdgeのF12開発者ツールの中でも使われていたりします。

そして月日は流れて、Visual Studio Codeがオープンソースになってついにエディタ部分(Monaco Editor)が公開されました。

ということでそれを使う方法を調べていたのですが、たまたまシンタックスハイライトの機能が備わっているのを発見したので単体でも使えるようにするライブラリを作りました。

mayuki/Mimosa - GitHub

使い方

GitHubのReleasesに一式を固めたものがあるのでそれをとってきて適当に展開します。

<script src="/shared/js/mimosa/vs/loader.js"></script>
<script src="/shared/js/mimosa/mimosa.min.js"></script>
<script>
    require.config({
        baseUrl: '/shared/js/mimosa/', // MimosaとMonacoの入っているディレクトリへのパス
    });

    // 自動で pre.vs[data-lang] な要素を探してシンタックスハイライトを適用する
    Mimosa.initialize();
</script>

こんな感じでMimosaを読み込ませるものをページに書いておきます。 loaderはVSCode Loaderなのですが、これがほかのrequire機構と被ると何が起きるのかは謎です。

そして以下のように対象となるソースコードを pre 要素にして vs クラス (とスタイルのために monaco-editor)をつけておきます。 あと data-lang 属性に言語名を指定する必要があるのでそれも指定します。 対応している言語名はcss, html, javascript,...csharp, bat, powershell, ...といった感じです。

<pre data-lang="csharp" class="monaco-editor vs">
class A
{
    public async Task<int> Hoge()
    {
        await Task.Delay(1000);
        return 10;
    }
}</pre>

そうすると以下のような感じでシンタックスハイライトが適用されます。

class A
{
    public async Task<int> Hoge()
    {
        await Task.Delay(1000);
        return 10;
    }
}

ちなみに vs-dark クラスをつけておくと黒背景になじむカラーリングになります。

class A
{
    public async Task<int> Hoge()
    {
        await Task.Delay(1000);
        return 10;
    }
}

備考

まとめ

どうぞご利用ください。

次回はMonaco Editorを使う方法について書く予定があるとかないとか(ビルド方法とかはそっちで…)。

JavaScriptエンジン"Chakra"がオープンソースになる

Created at:

Microsoft Edge’s JavaScript engine to go open-sourceということでInternet ExplorerのちのEdgeのJavaScriptエンジンであるChakraをオープンソースにします、というアナウンスが。

Node.jsのスクリプトバックエンドをChakraにする魔改造を公開していたりしたので、いずれ来るだろうなあと思っていたのでついにという感じですね。

要するに…

小さいとか軽量とか書いてもいるのでChakraCoreはV8のように、何かに組み込んで使ってほしいという雰囲気がありますね。まあ何はともあれ楽しみです。

RoslynをT4テンプレート内で使う

Created at:

Visual StudioにはT4テンプレートというソースコードを生成する機能があるのですが、その中からRoslynを使おうというお話です。

T4テンプレートはVisual Studioと統合されていることとC#でテンプレートのコードを書いて、C#, VB, TypeScriptなどのコードを吐き出せます。

例えば…

のような感じで、アセンブリを読み込んでコードを生成するという使い方はよく見かけます(プロキシコードの生成とか)。

アセンブリを読み込んでコードを生成するということは「アセンブリがコンパイルされている」ことが前提になります。

例えばプロキシクラスみたいなものはそれで問題ないので、大体はそれでいいのですが「元になるコードと生成したコードを同一のアセンブリに含めたい」場合にはニワトリ卵というか「元になるコードをコンパイル」「コンパイル結果をもとにコード生成」「コード生成を含めてコンパイル」という手順が必要です。

もちろんリフレクションとかファクトリとか登録する式にすればコード生成いらないよね?という場合もあると思いますが、そこはまあ要件しだいというやつです。生成したいときもある、はず…。

例えばEnvDTEを避ける

そんなわけで「コンパイルしてできたアセンブリを元にコードを生成」するのではなく、「現在のコードを元にコードを生成」したいというときはどうするのかというとコードの構造を読み取るためにVisual Studioにアクセスする方法をとります。

一般的にT4テンプレートからVisual StudioにアクセスするにはEnvDTEというかっこいいオブジェクトモデルと仲良くすることになります。EnvDTEはVisual Studioの機能を公開するCOMライブラリなのなので、絶妙な使いづらさがあります。何よりEnvDTEはVisual Studioにくっついている都合、ConsoleApplicationやLINQPadなどで試しに書いてみるというのが難易度高いです。

まあサンプルコードを書く気にならない程度には面倒だということです。

そこでVisual Studio 2015以降ではコンパイラー基盤がRoslynになっているのでそれを使えばいいのではというのが今回のアイデアです。

T4の中でRoslynを使ってコードを読み込む

さてどうやってRoslynを使って読み込むのかという話ですが、残念なことにVisual Studio 2015の内部で「現在動いているRoslynのインスタンス」に直接アクセスすることはできません。

ですが、Visual Studio 2015に含まれているRoslynを使うことはできます。Roslynは C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies におかれているので、それらしいアセンブリを参照して名前空間をインポートします。

<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.CSharp.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.Workspaces.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.Workspaces.Desktop.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\System.Collections.Immutable.dll" #>

<#@ import namespace="Microsoft.CodeAnalysis" #>
<#@ import namespace="Microsoft.CodeAnalysis.CSharp" #>
<#@ import namespace="Microsoft.CodeAnalysis.CSharp.Syntax" #>
<#@ import namespace="Microsoft.CodeAnalysis.MSBuild" #>

これで使えるようになります!

あとは単純にRoslynを使ってソリューションを読み込んで、コードを解析すればいい感じに処理できるでしょう。

と言ってもいきなりT4でRoslynプログラミングはそれはそれで難しいのでConsoleApplicationかLINQPadで書いてみるのがよいでしょう。

T4の外でコードを書いてみる

とりあえずどこかのC#プロジェクトにあるクラスの一覧を適当に出力する、というコードを書いてみることにします。Roslynのコードの詳しいことは今回割愛しますので別途調べてください。

というわけでLINQPadやConsoleApplicationで動きそうな単純なコードは以下のようになります。

void Main()
{
    // 対象となるプロジェクトの.csprojのパス
    var projPath = @"C:\Users\Tomoyo\Documents\visual studio 2015\Projects\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.csproj";
    var classNames = GetClassNamesAsync(projPath).Result;
    classNames.Dump();
}

// Roslynを使ってプロジェクトのコードからクラスのコンストラクタ定義を引っこ抜く
async Task<List<string>> GetClassNamesAsync(string csprojPath)
{
    // MSBuildのワークスペース(環境みたいなもの)を作って、プロジェクトファイルを開く
    var workspace = MSBuildWorkspace.Create();
    var project = await workspace.OpenProjectAsync(csprojPath);

    // ソースコードをコンパイル的なことをする(出力するわけではなくて内部的なデータを作るやつ)
    // これでコードをパースした結果を得ることができるようになる
    var compilation = await project.GetCompilationAsync();

    var classNames = new List<string>();
    // シンタックスツリーをファイル単位で適当になめていく
    foreach (var syntaxTree in compilation.SyntaxTrees)
    {
        // セマンティックモデル(シンタックスツリーは文法で、それに対応する「コード的な意味」)を取得する
        var semModel = compilation.GetSemanticModel(syntaxTree);

        // シンタックスツリーからクラス定義のシンタックス(記述)を引っ張り出して、
        // セマンティックモデルに問い合わせることでクラス定義(意味)を引っ張り出して、ふにゃふにゃ処理する。
        classNames.AddRange(
            syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<ClassDeclarationSyntax>()
                .Select(x => semModel.GetDeclaredSymbol(x))
                .Select(x => x.ToDisplayString())
        );
    }

    return classNames;
}

実行結果はこんな感じ。

LINQPad

なおこのコードはMicrosoft.CodeAnalysis.CSharp.WorkspaceというNuGetパッケージをインストールすれば動くでしょう。

T4テンプレートの中で使う

で、これとT4テンプレートの中で使うには…という話ですが、先ほどのimportに加えて、適当にコピペしても大体動くはずです。例えば以下のように。

template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Runtime" #>
<#@ assembly name="System.Threading.Tasks" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.CSharp.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.Workspaces.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\Microsoft.CodeAnalysis.Workspaces.Desktop.dll" #>
<#@ assembly name="$(DevEnvDir)PrivateAssemblies\System.Collections.Immutable.dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Threading.Tasks" #>
<#@ import namespace="Microsoft.CodeAnalysis" #>
<#@ import namespace="Microsoft.CodeAnalysis.CSharp" #>
<#@ import namespace="Microsoft.CodeAnalysis.CSharp.Syntax" #>
<#@ import namespace="Microsoft.CodeAnalysis.MSBuild" #>
<#@ output extension=".txt" #>
<#
    // 対象となるプロジェクトの.csprojのパス
    var projPath = @"C:\Users\Tomoyo\Documents\visual studio 2015\Projects\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.csproj";
    var classNames = GetClassNamesAsync(projPath).Result;
#>

<# foreach (var className in classNames) {
#>- <#= className #>
<# } #>

<#+

// Roslynを使ってプロジェクトのコードからクラスのコンストラクタ定義を引っこ抜く
async Task<List<string>> GetClassNamesAsync(string csprojPath)
{
    // MSBuildのワークスペース(環境みたいなもの)を作って、プロジェクトファイルを開く
    var workspace = MSBuildWorkspace.Create();
    var project = await workspace.OpenProjectAsync(csprojPath);

    // ソースコードをコンパイル的なことをする(出力するわけではなくて内部的なデータを作るやつ)
    // これでコードをパースした結果を得ることができるようになる
    var compilation = await project.GetCompilationAsync();

    var classNames = new List<string>();
    // シンタックスツリーをファイル単位で適当になめていく
    foreach (var syntaxTree in compilation.SyntaxTrees)
    {
        // セマンティックモデル(シンタックスツリーは文法で、それに対応する「コード的な意味」)を取得する
        var semModel = compilation.GetSemanticModel(syntaxTree);

        // シンタックスツリーからクラス定義のシンタックス(記述)を引っ張り出して、
        // セマンティックモデルに問い合わせることでクラス定義(意味)を引っ張り出して、ふにゃふにゃ処理する。
        classNames.AddRange(
            syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<ClassDeclarationSyntax>()
                .Select(x => semModel.GetDeclaredSymbol(x))
                .Select(x => x.ToDisplayString())
        );
    }

    return classNames;
}
#>

で、このT4テンプレートを実行すると、以下のようにクラス名の一覧が記述されたファイルが吐き出されます。

ConsoleApplication1.Hauhau
- ConsoleApplication1.Program
- ConsoleApplication1.Hoge
- ConsoleApplication1.Fuga

とても簡単ですね。

まとめ

大抵の場合においてはアセンブリを直接読み込めばいいと思いますが、もし「現在のコードを元にプロジェクトに含めるコードを生成する」という必要がある場面では役に立つのではないでしょうか。

またこの組み合わせで書いてみるとわかるのですがLINQPadでRoslynでソースコードから情報を集約するコードを書いて、それを元にジェネレートする、という手順はテンプレートからロジックの一部を切り出して実装でき、デバッグしやすいのでとてもおすすめです。

Android TVなBRAVIA

Created at:

7月ごろにSonyのBRAVIAはちょうど大きなモデルチェンジのタイミングでミドルレンジより上の機種はAndroid TVが搭載されるようになりました。 で、KDL-55X8500Cという機種を一応発売日に買ったのでそのメモというか人柱レビューというかなんというか。

端末情報:KDL-55X8500C

ちなみにAndroid TV搭載というのはどういうことかというと、テレビそのもののプラットフォームが謎のOSからスマートフォンと同じようにAndroidベースになったということです。

なのでGoogle Playのストアからアプリをインストールできたりといった今までのテレビとはちょっと違う感じになっています。

感想

Androidで動いてるというところからのお察しの通りで端的に言えば地雷です。

プラットフォームが刷新されたため、あらゆるものが作り直しされているが故の未完成さがかなりある上に安定性も微妙でテレビとしてどうなの感があります。 アップデートである程度機能が増えたり、安定したりしなかったらだいぶ厳しい感じですね。

ある程度は予想はしていたのですが思っていたよりもひどいですね。

よくないところ & 使えない機能

以前使っていたテレビはKDL-40W900Aという2013年のモデルなので大体2世代前ということでそこまで古くはないですが、そこからの比較ということで。

普通に機能がデグレってるのがかなりおおうっ…となるポイントです。

まさか2年後の機種で機能がしょぼくなってもっさりして不安定になるとは…って感じなのでアップデートで何とかしてほしいですが無理な予感がしていますね。すでに2か月が経過してますけど。

追記: そういえば設置しにきてもらってPlayStation 3をつないだら音は出るけど映らない事象に出くわして、挿すところを変えたりPS3の設定をリセットしてもダメで(Wii Uは映った)、設置に来た方(ソニーの人)がすみません調査しますって帰って、アップデートしたら直ったというところからのスタートだったということを思い出しました。

いいところ

いいところもあるにはあります。

Androidであることというのはちょっと面白いというか、中身が透けて見えるというか。 アプリ作れるとはいえJava書きたくないし、エミュレーター遅いしで結構つらいのですけど作れないよりはましです。なおadbで接続するならUSB(A)のオス-オスが必要なので注意です。

特にGoogle Castが組み込まれているところはよいです。Google Castというのはブラウザやアプリから画面を受信できるという機能です。

Google Castのテレビ用の外付けデバイスに相当するChromecastというものがありますが、Android TVなテレビはGoogle Castが組み込まれています。 テレビ自体がCastを受け取れるため何もせずスマートフォンやブラウザからCastするだけで自動で表示できます。 つまりChromecastやNexus Playerの場合にはまずその機器への入力切替が必要となりますがその操作が不要なのです。

追記: ChromecastはHDMI機器制御対応らしく、利点がつぶされたかと思いましたがCastやめたらテレビに戻ったりはできないっぽいのでセフセフ。

起動が速いのはスマホやパソコンのようにスリープ的な状態から復帰するだけなのでフル機能を使えるまでの時間が圧倒的に短いのです(もちろん再起動には時間かかる)。 対して40W900Aなどの以前のモデルはある時間帯(しかも時間指定不能)だけ高速起動可能状態になれるのですがその時間を外すとフル機能を使えるまでの時間が長いです。これは普通に良いところです。

おすすめのアプリ

ホーム: アプリ

折角ですのでおすすめのアプリを少しメモしておきます。Google Playアプリからだと検索できなかったりそもそも面倒だったりするので、パソコンからGoogle Playにブラウザでアクセスしてインストールするのをお勧めします。

その他

性能的にはスマホでいえばXperia Zかそれ以下ぐらい(デュアルコア)でRAMは1.5GBぐらいっぽいです。絶妙にしょぼい…。実際体感的にも遅いかなって感じがします。

まとめ

すごくAndroidっぽい。たぶんすぐにアップデートされなくなってAndroidっぽさが完成するのではないかという予感がしています。

というのは半分冗談としても、現状だいぶひどいですし安い買い物ではなかったのでアップデートで前の世代並みにはよくしてほしいなというのが正直なトコロです。

Metro.cs #1 .NETプログラム (のランタイムを実装する) 入門なのです

Created at:

先日、CoreCLRを見ながら勉強がてら作ってみたInazumaというものを公開したところ、 さおさんが平日版めとべややるので話してくださいよーって振ってきたので、自信ないですけど…みたいな感じでお話することに。

勉強のために作っていたのでいろいろ知識を整理もできていなくてふわふわした状態でしゃべるという今までになく不安なセッションでなんかすみません的な感じでした。

それはともかく、CoreCLRを動かしてみるのは面白いのでぜひやってみるといいのではないかと思うのと、意外とみんなインタープリターとか作ってみたいと思わないものっぽいという感触を得ました。 あとインタープリターのデモは工夫しないと微塵もよくわからんって感じですね(知ってた)。