なんとな~くしあわせ?の日記

ClojureとかAWSの設定とかをメモする技術ブログ

Scala Refactoring



scala-refactoring

Scala Refactoring

Scalaソースコードリファクタリングするためのライブラリ。もちろんリファクタリングの内容を考えてくれるのではなく、一定の処理を自動化する。eclipseIntelliJ IDEAなどのIDEで実行されるような処理を提供するライブラリ。ensimeを修正するために必要だったので、ドキュメントを読んでいく。

公式のドキュメント

リファクタリング機能一覧→ Refactorings

Rename
・ 名称変更

Extract Local
・ 展開

Inline Local
・ 内包

Extract Method
・ 関数の展開

Organize Imports
・ import編成

Move Class
・ クラス移動

実装サンプル

すべて、 Documentation | Scala Refactoring より

4. Tool Integration の翻訳になる

4. ツールの結合

この章では、実装済みのrefactoringsがどのように他のソフトウェアに結合されうるのか、そして現在のScala IDE for Eclipseにどのように統合されているのかを見ていこうと思います。

4.1. 依存関係

リファクタリングのライブラリはScalaコンパイラのみに依存します、サードパーティーのライブラリは使われていません。しかし、またどんなユーザーインターフェースも持っていません。つまり、シームレスなツールの結合には、結合する側のツールによってそれが実装される必要があるということです。IDEへの組み込みを行うにあたり、リファクタリングの実装はそれ自身のコンパイラソースコードをパースしチェックするようなインスタンスは生成しません。これはたいていすでにIDEによってなされており、そのようなことをすれば二度手間になり明らかにリファクタリングの処理が遅くなってしまうでしょう。プロジェクトのコンパイル単位にアクセスするために、Refactoring trait (すべての実装が継承する)はコンパイラインスタンスの抽象メンバをもちます:

  trait Refactoring extends . . . {
  val global: scala.tools.nsc.interactive.Global
  . . .
  }

リファクタリング自体の実装のインスタンスを作るには、コンパイラへの参照が提供される必要があります。自動テストのためにこれがどのようになされたのか、章5のp.71に説明されています。説明としては、章3のp.39の始めのほうでそれぞれの段階でのリファクタリングが実演されています。というのは、次のページの図4.1にてユーザとIDE、そしてリファクタリングライブラリの間でのシーケンスがビジュアル化されています。prepareperform メソッドMutliStageRefactoring という抽象クラスの一部で必要とされるパラメーターです。(p.63の図4.2を見よ)
 

4.2. ライブラリの結合

どのようにしてリファクタリングが組み込まれるかをちゃんとした例をもって示します: 
リファクタリングのエディタはp.63の図4.3で示されます。詳細な手順を以下に示していきます。

図4.1

f:id:panzer-jagdironscrap1:20151008210927p:plain


名称変更のリファクタリングのみのための例です、しかしほかの処理もあとから結合可能です。以下のコードはアクションリスナーにおいて実行されます。最初に私たちはrefactoringのオブジェクトを生成しなければいけない。

val refactoring = new Rename with CompilerProvider with GlobalIndexes {
  val ast = treeFrom(editor.getText)
  val index = GlobalIndex(ast)
}

この例中にあるコンパイラとはCompilerProvider traitから提供されているもので、これはまたtreeFromメソッド(StringをTreeのインスタンスに変換するもの)を渡してくれます。私たちはこれを自分自身で行わなければならない、なぜならエディタはコードをパースすることがまだできないからで、実際のIDEリファクタリングを組み込んだとしたらまた違ったものになるでしょう。名称変更のリファクタリングはさらにプログラム全体のインデックスを必要とします。そこで私たちはGlobalIndexesという、トレイト(これはASTからインデックスを構築します)を使用します。(2.2章のp11を見よ)多くのリファクタリングは動かすためにソースコードの選択が必要です。Selection トレイトは2つの実装があります、FileSelectionTreeSelectionです。選択が何によってもたらされるかによって、どちらかを使うほうが簡単でしょう。私たちは今回はエディタの選択範囲からFileSelectionを作ります。

val selection: refactoring.Selection = {
  val file = refactoring.ast.pos.source.file
  val from = editor.getSelectionStart
  val to = editor.getSelectionEnd
  new refactoring.FileSelection(file, from, to)
}

選択範囲が与えられることで、私たちはリファクタリングの最初の段階を宣言できます => prepare メソッドです。

prepare を呼び出すと Either[PreparationError, PreparationResult] が返ります、ですので私たちは結果を展開するかエラーであれば処理を停止しなければならない(…名称変更のリファクタリングのエラーの原因はたいてい不適切な選択によるものです)。

val preparationResult = refactoring.prepare(selection) match {
  case Left(refactoring.PreparationError(error)) =>
    showError(error)
    return
  case Right(r) => r
}

preparerationResultの結果は選択したリファクタリングの実際の型によります。名称変更の場合、名称変更したいものの木構造をふくんだものが単純に返ります。次にリファクタリングを実行しましょう、これには必要なパラメータを通す必要があります。それは新しい名称です。新しい名称を入力するダイアログを開くaskName関数です。

val refactoringParameters = {
  val selectedName = preparationResult.selectedTree.symbol.nameString
  askNewName(selectedName)
}

リファクタリングを実行することは、多くのパラメーターを通し、成功の際変更のリストが戻ること以外、準備することととても似ています。

val changes: List[Change] =
  refactoring.perform(selection, preparationResult, refactoringParameters) match {
    case Left(refactoring.RefactoringError(error)) =>
      showError(error)
      return
    case Right(r) => r
  }

私たちのエディタでは、変更を単純にファイルに適用します、その際はChange.applyChangesを使用します。実際のエディタでは、ユーザは変更を適用する前に提示された変更を確認するチャンスがあるべきです。・・・

(以下略、残りに関しては英文をご確認願います)

その他

Scala Refactoring 自体は開発がどのように進んでいるかよくわからない。