AndroidとかiOSとかモバイル多め。その他技術的なことも書いていきます。

DangerでTextViewのandroid:maxLinesがないことをチェックしようとした

APIからデータを受け取ってTextViewに表示するとき、大きいデータだと際限なく表示されてしまうので android:maxLines を設定されたい。結構忘れがちなのでDangerで自動チェックしようとした...けどやりたかったことを全部満たすのは大変そうだったので妥協した話。

やりたかったこと

  • レイアウトファイルのdiffのTextView内に android:maxLines がなかったら警告する
  • 警告にはファイルと行数を指定してPR上から飛べるようにする(e.g. warn("android:maxLinesがありません", file: "app/src/main/res/layout/content_main.xml", line: 10))
  • 既存のTextViewには警告を出さない

結論から言うと最後の 既存のTextViewには警告を出さない を諦めた

どうチェックするか?

Dangerではgit.diffでdiffが取れるのでパースできる。もしくは

active_files = (git.modified_files + git.added_files).uniq
layout_files = active_files.select { |file| file.include?("app/src/main/res/layout/") }

とすれば変更されたレイアウトファイル一覧が取れるので そのファイル全体を読み込んでパースすることもできる。

まずgit.diffをパースする場合、問題としては行数が取れない。diffと実際のファイルを比べて行数を取ってくるのはしんどい...妥協としてTextViewの始まりから終わりまでを表示させてみたが削除部分が誤判定されてしまい排除しなくてはいけなく、更にしんどい...

in_textview = false
maxline_exists  = false
textview_line = []
git.diff.patch.lines.each do |diff_line|
  if /^.*<TextView$/ === diff_line
    in_textview = true
    textview_line.clear
    message("In TextView, #{diff_line}")
  end
  if in_textview 
    textview_line.push(diff_line)
    if diff_line.include?("</TextView>") || diff_line.include?("/>")
      in_textview = false
      if !maxline_exists
        message("Please add android:maxLines\n```xml\n#{textview_line.join()}```")
      end
    elsif diff_line.include?("android:maxLines")
      maxline_exists  = true
    end
  end    
end

f:id:phicdy:20190817220449p:plain

レイアウトファイル全体をパースする場合、PRでの変更外も警告してしまうので鬱陶しい可能性がある。 android:maxLines が必要ないラベルなどがあると常に警告されてしまう。とはいえ行数が取れるので表示自体は理想通りになる。

active_files = (git.modified_files + git.added_files).uniq
layout_files = active_files.select { |file| file.include?("app/src/main/res/layout/") }

in_textview = false
maxline_exists  = false
textview_line_num = 0
layout_files.each do |filename|
  file = File.read(filename)
  lines = file.lines
  lines.each_with_index do |l, num|
    if l.include?("<TextView")
        in_textview = true
        textview_line_num = num + 1
    end
    if in_textview 
        if l.include?("</TextView>") || l.include?("/>")
            in_textview = false
            if !maxline_exists
                message("Please add android:maxLines", file: "#{filename}", line: textview_line_num)
            end
        elsif l.include?("android:maxLines")
            maxline_exists  = true
        end
    end    
  end
end

f:id:phicdy:20190817220626p:plain

結論

警告表示が理想的なレイアウトファイル全体をパースする方向で妥協した。不要な警告なら無視すればよいし、例えばstyleが適用されてたらスキップするとか改善のしようはありそう。