まえがき
現在、HabrのWebサービスに関する最初の記事を執筆中です。 私の意見では標準の<source>タグでは定性的な構文強調効果が得られないため、最初からサードパーティのハーバーハイライターでソースコードを強調することにしました。 しかし、私が見つけた代替ハイライトは1つだけです-http://highlight.hohli.com 。 残念ながら、その着色の結果は私を刺激しませんでした。 投稿の最後に、直接比較するために6つのカラーオプションの中からそれを示します。 以前、別の蛍光ペンがありました - ソースコード蛍光ペンは、 こことここでHabréについて説明されていますが、長い間機能していません 。 その後、WordにコピーするときにVisual Studioのコードが完全にその外観を保持することを思い出しました。この単純な手法が私の方法の基礎になりました。
方法
アイデアは簡単です。コードはWordにコピーされ、ドキュメントはhtml Webページとして保存され、関数はhtmlコードをHabra互換のものに変換します。 例を考えてみましょう。 この単純なコードを保存すると、次の<body>コンテンツが生成されます。ここで、すべての<span>タグを青で、スタイルの色をピンクで、表示されたテキストを茶色でマークしました。
プライベート void Hello(){
MessageBox .Show( "Hello Habrahabr!" );
}
Hello.htm
<body lang = RU style = 'tab-interval:35.4pt'>
<divクラス= WordSection1>
<p class = MsoNormalスタイル= 'margin-bottom:0cm; margin-bottom:.0001pt; line-height:
normal; mso-layout-grid-align:なし; text-autospace:なし '> <span lang = EN-US
style = 'font-size:9.5pt; font-family:Consolas; 色:黒 、背景:白、
mso-highlight:白; mso-ansi-language:EN-US ' > <span
style = 'mso-spacerun:yes' > </ span> </ span> <span class = GramE > <span
lang = EN-US style = 'font-size:9.5pt; font-family:Consolas; 色:青 、背景:
白; mso-highlight:白; mso-ansi-language:EN-US ' > プライベート </ span> </ span> <span
lang = EN-US style = 'font-size:9.5pt; font-family:Consolas; 色:黒 、背景:
白; mso-highlight:白; mso-ansi-language:EN-US ' > </ span> <span lang = EN-US
style = 'font-size:9.5pt; font-family:Consolas; 色:青 、背景:白、
mso-highlight:白; mso-ansi-language:EN-US ' > void </ span> <span lang = EN-US
style = 'font-size:9.5pt; font-family:Consolas; 色:黒 、背景:白、
mso-highlight:白; mso-ansi-language:EN-US ' > Hello(){ <o:p> </ o:p> </ span> </ p>
<p class = MsoNormalスタイル= 'margin-bottom:0cm; margin-bottom:.0001pt; line-height:
normal; mso-layout-grid-align:なし; text-autospace:なし '> < span lang = EN-US
style = 'font-size:9.5pt; font-family:Consolas; 色:黒 、背景:白、
mso-highlight:白; mso-ansi-language:EN-US '> <span
style = 'mso-spacerun:yes'> </ span> </ span> <span class = GramE> < span
lang = EN-US style = 'font-size:9.5pt; font-family:Consolas; 色:#2B91AF ;
背景:白; mso-highlight:白; mso-ansi-language:EN-US '> メッセージボックス </ スパン > < スパン
lang = EN-US style = 'font-size:9.5pt; font-family:Consolas; 色:黒 、背景:
白; mso-highlight:白; mso-ansi-language:EN-US '> .Show( </ span> </ span> <span
lang = EN-US style = 'font-size:9.5pt; font-family:Consolas; 色:#A31515 ;
背景:白; mso-highlight:白; mso-ansi-language:EN-US ' > &quot;こんにちは
Habrahabr !& Quot ; </ span> <span lang = EN-US style = 'font-size:9.5pt; font-family:
コンソラス; 色:黒 ;背景:白; mso-highlight:白; mso-ansi-language:
EN-US '> ); <o:p> </ o:p> </ span> </ p>
<p class = MsoNormal> <span lang = EN-US style = 'font-size:9.5pt; line-height:115%;
フォントファミリー:コンソラ; 色:黒 ;背景:白; mso-highlight:白;
mso-ansi-language:EN-US '> <span style =' mso-spacerun:yes ' > </ span> </ span> <span
style = 'font-size:9.5pt; line-height:115%; font-family:Consolas; 色:黒 ;
背景:白; mso-highlight:白 ' > } </ span> </ p>
</ div>
</ body>
ご覧のとおり、ソースコードの各行は<p>タグに変換され、その内部でテキストはネストされた<span>タグを使用してフォーマットされ、その色はstyle属性に格納されます。 変換アルゴリズムが明確になります。 「/ html / body / div / p」の各<p>タグ に対して、そのコンテンツを再帰的に新しいhtmlドキュメントに変換し、同時に<font>タグを使用してテキストの色を設定します。 ネタバレの下には、結果のメソッドの完全なソースコードがあり、これは元のカラースキームに従って表示されます。 htmlを使用するために、 Html Agility Packライブラリが使用されました。
HabraCodeFormatter.cs
- システムを使用して ;
- System.IO を使用します。
- HtmlAgilityPack を使用 。
- 名前空間 HabraCode {
- パブリック スタティック クラス HabraCodeFormatter {
- const char spaceClassic =( char ) 32 ;
- const char spaceNbsp =( char ) 160 ;
- public static void Format ( string fileHtm、 bool withLineNumbers = false ){
- var info = new FileInfo (fileHtm);
- 文字列 destFile = info。 DirectoryName + "\\" +情報。 お名前 Replace ( info。Extension 、 null )+ "_result.txt" ;
- 形式 (fileHtm、destFile、withLineNumbers);
- }
- public static void Format ( string fileHtm、 string fileDest、 bool withLineNumbers = false ){
- var doc = new HtmlDocument ();
- doc。 ロード (fileHtm);
- var nodes = doc。 DocumentNode SelectNodes ( "/ html / body / div / p" );
- int spaceToDelete = getSpacesToDelete (ノード[ 0 ]);
- var result = new HtmlDocument ();
- var rootNode = result。 CreateElement ( " blockquote " );
- 結果。 DocumentNode AppendChild (rootNode);
- if (withLineNumbers){
- var nodeOl =結果。 CreateElement ( "ol" );
- rootNode AppendChild (nodeOl);
- rootNode = nodeOl;
- }
- foreach (ノード内の varノード){
- if (withLineNumbers){
- var nodeLi = doc。 CreateElement ( "li" );
- nodeLi。 AppendChildren (ノード。ChildNodes );
- ノード。 RemoveAllChildren ();
- ノード。 AppendChild (nodeLi);
- }
- int refSpaces = SpacesToDelete;
- translateNode (node、rootNode、 ref refSpaces);
- rootNode AppendChild ( result。CreateTextNode ( Environment。NewLine ));
- }
- 結果。 保存 (fileDest);
- }
- private static int getSpacesToDelete ( HtmlNodeノード){
- for ( int index = 0 ; index < node。InnerText。Length ; index ++){
- if (! isSpace ( node。InnerText [index])){
- インデックスを返します。
- }
- }
- 0を 返し ます 。
- }
- private static void translateNode ( HtmlNodeノード、 HtmlNode destParent、 ref int spaceToDelete){
- HtmlNode destNode = destParent;
- スイッチ (ノード。 名前 ){
- ケース "o:p" :
- ケース "p" :
- 休憩 ;
- ケース 「スパン」 :
- 文字列の色= getColor (ノード);
- if (color!= null ){
- destNode = destParent。 OwnerDocument 。 CreateElement ( "font" );
- destNode。 SetAttributeValue ( "color" 、色);
- }
- 休憩 ;
- ケース "#text" :
- string text = translateText (node。InnerText、 ref spacesToDelete);
- destNode = destParent。 OwnerDocument 。 CreateTextNode (テキスト);
- 休憩 ;
- ケース 「b」 :
- ケース 「li」 :
- destNode = destParent。 OwnerDocument 。 CreateElement (ノード。 名前 );
- 休憩 ;
- デフォルト :
- 新しい InvalidOperationException ( "予期しないnode.Name" + node。Name)を スローします。
- }
- if (! ReferenceEquals (destNode、destParent)){
- destParent。 ChildNodes 。 追加 (destNode);
- }
- if ( node。HasChildNodes ){
- foreach (ノードのHtmlNodeの子。ChildNodes ){
- translateNode (child、destNode、 ref spaceToDelete);
- }
- }
- }
- private static string translateText ( 文字列テキスト、 ref intスペース){
- if ( string。IsNullOrEmpty (text)){
- 戻りテキスト。
- }
- テキスト=テキスト。 Replace ( "http://" 、 "http&#58; //" );
- テキスト=テキスト。 Replace ( "\ r \ n" 、 "" );
- if (スペース> 0 &&テキスト。 長さ > =スペース&& isSpace (テキスト。 サブストリング ( 0 、スペース))){
- テキスト=テキスト。 削除 ( 0 、スペース);
- スペース= 0 ;
- }
- テキスト=テキスト。 Replace ( spaceClassic。ToString ()、 "&nbsp;" );
- テキスト=テキスト。 Replace ( spaceNbsp。ToString ()、 "&nbsp;" );
- 戻りテキスト。
- }
- プライベート スタティック ブール isSpace ( char ch){
- return (ch == spaceClassic || ch == spaceNbsp );
- }
- private static bool isSpace ( string text){
- foreach (テキスト内の文字){
- if (! isSpace (ch)){
- falseを 返し ます 。
- }
- }
- trueを 返し ます 。
- }
- private static string getColor ( HtmlNodeノード){
- var attr = node。 属性 [ "スタイル" ];
- if (attr == null || attr。Value == null ){
- nullを 返し ます 。
- }
- 文字列 []値= attr。 価値 Split ( new [] { ';' });
- foreach (値の文字列値){
- const string prefixColor = "color:" ;
- トリミングされた文字列 =値。 トリム ();
- if (トリミングされたStartsWith ( prefixColor )){
- トリミングを返します。 削除 ( 0 、 prefixColor。Length );
- }
- }
- nullを 返し ます 。
- }
- }
- }
おそらく、多くの人が私のカラーコードをカラフルすぎると呼んでいるため、不快感を覚えますが、ソースコードを操作するプロセスが大幅に改善されます。 これは、パフォーマンスを少し改善する非常に簡単な方法です。 コードに関する小さなコメント。 このクラスには、Formatメソッドの2つのオーバーロードが含まれます。必須パラメーターはソースhtmlファイルへのパスであり、オプションで行番号を追加するためのフラグです。 2番目のオーバーロードでは、最終ファイルを明示的に指定できます。指定しない場合、結果は[source_name] _result.txtとして保存されます。 getSpacesToDeleteメソッドを使用すると、最初の行の先頭のスペースの数を検索して、後続のすべての行でそれらを削除できます。
Visual Studioのソースコードのフォントと色は、メニューの[ ツール ] > [オプション ] > [環境]> [フォントと色 ]で設定します。 ただし、最初は通常、メソッドまたはプロパティの色を設定する機能はありません。 しかし、ReSharperを使用すると、そのような機会が現れます。 これを行うには、メニューReSharper>オプション>コード検査>設定で、アイテムの色の識別を有効にします。 そして、ここでは、メソッドがターコイズ色になり、クラスが従来のスキームでそのような色になり、クラスが濃い青色になるので、わずかな認知的不協和を体験できます-これをすべて下で見ることができます、色を比較します。
私のスキームでは、クラス、キーワード、およびその他の要素には古典的な色が付いていますが、メソッドおよびプロパティのためにそれらの色が追加されています。 それを試してみたい人のために、私は設定をもたらします。 xmlをスポイラーの下に.vssettings拡張子を持つファイルとして、BOMなしのUTF-8エンコーディングで保存します(HabraCode.vssettingsなど)。メニューから[ ツール ] > [インポートとエクスポート設定]> [インポート...]を選択します。 。 デフォルトでは、ウィザードは現在の設定もすべて保存します-変更のロールバックを許可します。 インポート後、フォントはデフォルトにリセットされます。カスタムフォントがある場合は、再度指定する必要があります。 また、「プレーンテキスト」設定で、背景の白色度をわずかに下げ、絶対値から#FAFAFAに下げました。
HabraCode.vssettings
<UserSettings> <ApplicationIdentity version="10.0"/> <ToolsOptions> <ToolsOptionsCategory name="Environment" RegisteredName="Environment"/> </ToolsOptions> <Category name="Environment_Group" RegisteredName="Environment_Group"> <Category name="Environment_FontsAndColors" Category="{1EDA5DD4-927A-43a7-810E-7FD247D0DA1D}" Package="{DA9FB551-C724-11d0-AE1F-00A0C90FFFC3}" RegisteredName="Environment_FontsAndColors" PackageName="Visual Studio Environment Package"> <PropertyValue name="Version">2</PropertyValue> <FontsAndColors Version="2.0"> <Categories> <Category GUID="{A27B4E24-A735-4D1D-B8E7-9716E1E3D8E0}"> <Items> <!-- --> <Item Name="Plain Text" Foreground="0x02000000" Background="0x00FAFAFA" BoldFont="No"/> <Item Name="Literal" Foreground="0x02000000" Background="0x02000000" BoldFont="Yes"/> <Item Name="Number" Foreground="0x02000000" Background="0x02000000" BoldFont="Yes"/> <Item Name="ReSharper Namespace Identifier" Foreground="0x02000000" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Mutable Local Variable Identifier" Foreground="0x02000000" Background="0x02000000" BoldFont="No"/> <!--: --> <Item Name="String" Foreground="0x001515A3" Background="0x02000000" BoldFont="No"/> <Item Name="String(C# @ Verbatim)" Foreground="0x001515A3" Background="0x02000000" BoldFont="No"/> <!--: - --> <Item Name="ReSharper Extension Method Identifier" Foreground="0x008812C7" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Method Identifier" Foreground="0x008812C7" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Operator Identifier" Foreground="0x008812C7" Background="0x02000000" BoldFont="No"/> <!--, , : - --> <Item Name="ReSharper Event Identifier" Foreground="0x001256C7" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Field Identifier" Foreground="0x001256C7" Background="0x02000000" BoldFont="No"/> <!--: --> <Item Name="ReSharper Class Identifier" Foreground="0x00AC8500" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Delegate Identifier" Foreground="0x00AC8500" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Enum Identifier" Foreground="0x00AC8500" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Static Class Identifier" Foreground="0x00AC8500" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Interface Identifier" Foreground="0x00AC8500" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Struct Identifier" Foreground="0x00AC8500" Background="0x02000000" BoldFont="No"/> <Item Name="ReSharper Type Parameter Identifier" Foreground="0x00AC8500" Background="0x02000000" BoldFont="No"/> <!-- todo bug. NotImplementedException: --> <Item Name="ReSharper Todo Item" Foreground="0x000000F5" Background="0x02000000" BoldFont="Yes"/> <Item Name="ReSharper Todo Item Marker on Error Stripe" Foreground="0x000000F5" Background="0x02000000" BoldFont="No"/> <!-- string.Format: + bold--> <Item Name="ReSharper Format String Item" Foreground="0x00AC8500" Background="0x02000000" BoldFont="Yes"/> <!--: + bold --> <Item Name="ReSharper Constant Identifier" Foreground="0x00004080" Background="0x02000000" BoldFont="Yes"/> </Items> </Category> </Categories> </FontsAndColors> </Category> </Category> </UserSettings>
比較
例として、いずれかの方法を使用してコードを色付けするための6つのオプションを比較することをお勧めします。 ご存知のように、味と色には同志がいないので、ある種の色が原則として最高であるとは言い切れません。 しかし、私はhabrコミュニティの集合的な意見に興味があります。
1. <source lang = "cs">
private static string getColor(HtmlNode node){ var attr = node.Attributes["style"]; if (attr == null || attr.Value == null){ return null; } string[] values = attr.Value.Split(new [] {';'}); foreach (string value in values){ const string prefixColor = "color:"; string trimmed = value.Trim(); if (trimmed.StartsWith(prefixColor)){ return trimmed.Remove(0, prefixColor.Length); } } return null; }
2.黒と白のコード (HabraCodeFormatterで色と太字を拒否して取得)
private static string getColor(HtmlNodeノード){
var attr = node.Attributes ["style"];
if(attr == null || attr.Value == null){
nullを返します。
}
string [] values = attr.Value.Split(new [] {';'});
foreach(値の文字列値){
const string prefixColor = "color:";
トリミングされた文字列= value.Trim();
if(trimmed.StartsWith(prefixColor)){
return trimmed.Remove(0、prefixColor.Length);
}
}
nullを返します。
}
3.http://highlight.hohli.com
private static string getColor ( HtmlNode node ) {
var attr = node 属性 [ "スタイル" ] ;
if ( attr == null || attr。Value == null ) {
nullを 返し ます 。
}
string [ ] values = attr 。 価値 Split ( new [ ] { ';' } ) ;
foreach ( valuesの文字列値) {
const string prefixColor = "color:" ;
string trimmed = value 。 トリム ( ) ;
if ( trimmed。StartsWith ( prefixColor ) ) {
トリミングを返し ます。 削除 ( 0 、prefixColor.Length ) ;
}
}
nullを 返し ます 。
}
4. Visual Studioの標準カラーリング
プライベート 静的 string getColor( HtmlNode ノード){
var attr = node.Attributes [ "style" ];
if (attr == null || attr.Value == null ){
帰る ヌル
}
string [ ] values = attr.Value.Split( new [] { ';' });
foreach ( 値の 文字列 値){
const string prefixColor = "color:" ;
トリミングされた 文字列 = value.Trim();
if (trimmed.StartsWith(prefixColor)){
return trimmed.Remove(0、prefixColor.Length);
}
}
帰る ヌル
}
5.標準色のリシェーパー
private static string getColor ( HtmlNodeノード){
var attr = node。 属性 [ "スタイル" ];
if (attr == null || attr。Value == null ){
nullを 返し ます 。
}
文字列 []値= attr。 価値 Split ( new [] { ';' });
foreach ( 値の 文字列値){
const string prefixColor = "color:" ;
string trimmed = value 。 トリム ();
if (トリミングされたStartsWith ( prefixColor )){
トリミングを返します。 削除 (0、 prefixColor。Length );
}
}
nullを 返し ます 。
}
6.著者のカラーリング
private static string getColor ( HtmlNodeノード){
var attr = node。 属性 [ "スタイル" ];
if (attr == null || attr。Value == null ){
nullを 返し ます 。
}
文字列 []値= attr。 価値 Split ( new [] { ';' });
foreach (値の文字列値){
const string prefixColor = "color:" ;
トリミングされた文字列 =値。 トリム ();
if (トリミングされたStartsWith ( prefixColor )){
トリミングを返します。 削除 ( 0 、 prefixColor。Length );
}
}
nullを 返し ます 。
}