NSAttributedStringで簡易シンタックスハイライト

本日の動機

( ^o^)<シンタックスハイライトってかっこいいなぁ

( ˘⊖˘) 。o(よし僕も作ってみよう)

|Xcode|┗(☋` )┓三

( ◠‿◠ )☛また使いどころも考えずにろくでもないものを作るのか

▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわあああああ


──というわけで、今日は iOS デバイスに装飾付き文字を表示させてみましょう。写真はあまりにもしょぼいですが、がんばればもう少しどうにかなります。
f:id:tercel_s:20130211120114p:plain:w300

実装

註: 以下のソースコードは、すべて ViewController クラスの実装部に書きます。

まずは、強調表示させたい「予約語」のリストを作ります。

@implementation ViewController {
    @private NSMutableArray *keyWords;
}

今回は簡単のため、「SELECT」「FROM」 「WHERE」だけにしました。

- (id)initWithNibName:(NSString *)nibNameOrNil
               bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil
                           bundle:nibBundleOrNil];
    if(self) {
        
        // 色をつけたいキーワードを配列に登録する
        keyWords = [[NSMutableArray alloc] initWithObjects:
                    @"SELECT",
                    @"FROM",
                    @"WHERE",
                    nil];
    }
    
    return self;
}

次に、与えられた文字列に装飾を施し、その結果を返すメソッド「getHighlitedString:」を作ります。

ここではまず、装飾付き文字列のための NSAttributedStringインスタンスを与えられた文字列で初期化し、次にキーワードに合致する箇所に青色の装飾を加えるという方法を使っています。

- (NSAttributedString *)getHighlightedString:(NSString *)str
{
    NSMutableAttributedString *attributedString;
    attributedString = [[NSMutableAttributedString alloc] initWithString:str];
    
    for(NSString *keyWord in keyWords) {
        
        // 文字列を先頭から順に見つからなくなるまで検索する
        // 大文字・小文字は区別しない
        NSRange textSearchRange = NSMakeRange(0, [str length]);
        NSRange range;
        do {
            range = [str rangeOfString: keyWord
                               options: NSCaseInsensitiveSearch
                                 range: textSearchRange];
        
            if(range.location != NSNotFound) {
                [attributedString addAttribute:NSForegroundColorAttributeName
                                         value:[UIColor blueColor]
                                         range:NSMakeRange(range.location, range.length)];
            
                textSearchRange.location = range.location + range.length;
                textSearchRange.length = str.length - textSearchRange.location;
                
            }
        } while (range.location != NSNotFound);
    }
    return attributedString;
}

あとは、これをどこかで UI コンポーネントにセットすれば OK。今回は UITextView を使いましたが、テキストフィールドでもボタンでも大丈夫です。

- (void)viewDidLoad
{
    [super viewDidLoad];

    // テスト用の文字列
    NSString *str = @"SELECT 列名1, 列名2, ... \n"
                     "FROM テーブル名 \n"
                     "WHERE 列名 = ( \n"
                     "            SELECT 列名 \n"
                     "            FROM テーブル名 \n"
                     "            [WHERE 条件式など] \n"
                     "            ) \n";
    
    [textView setAttributedText:[self getHighlightedString:str]];
}

これでめでたく、色付き文字が表示されます。

ちなみに、getHighlitedString: はパフォーマンス的にちょっとアレなところがあるので*1、リアルタイムにシンタックスハイライトを実現したい場合はもうちょっと工夫が必要かと思います。

参考文献

(『iPhoneプログラミングUIKit詳解リファレンス』には、残念ながらNSAttributedStringクラスに関する記述がありませんでした)

*1:毎回メモリをアロケートしたり、文字列の全探索を何度も繰り返しているため。

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.