LSP 実装メモ (Text Document Synchronization `textDocument/willSave` `textDocument/willSaveWaitUntil` 編)

前回 tennashi.hatenablog.com

週刊 LSP 第三号

前回 gopls で見つけた Issue は無事 merge された

引き続き、Text Document Synchronization 周りの仕様について書いていく。

  • textDocument/didOpen 通知 <- done
  • textDocument/didChange 通知 <- done
  • textDocument/willSave 通知 <- 今日ここ
  • textDocument/willSaveWaitUntil リクエスト <- 今日ここ
  • textDocument/didSave 通知
  • textDocument/didClose 通知

前回は、次回 textDocument/didClose 予定と書いたが、やっぱり順番に見ていこうと思う。
残念ながらこれまで見てきたサーバ実装に willSave willSaveWaitUntil を実装したものが存在しないため、まとめて仕様の紹介のみとする。

textDocument/willSave 通知

この通知は TextDocument が保存される前にクライアントから送信される。

export interface WillSaveTextDocumentParams {
    textDocument: TextDocumentIdentifier;
    reason: number;
}

export namespace TextDocumentSaveReason {
    export const Manual = 1;
    export const AfterDelay = 2;
    export const FocusOut = 3;
}

TextDocumentSaveReason は保存をトリガした原因を示す。
Manual は手動で保存されたことを示し、AfterDelay は定期的に実行される自動保存によることを示し、FocusOut はエディタからフォーカスが移動したことによる自動保存を示す。

textDocument/willSaveWaitUntil リクエス

textDocument/willSave のリクエスト版である。
レスポンスに TextEdit[] を返すことで、クライアントが TextDocument を保存する前にサーバが指定した編集を加えることが出来る点で異なるのだが、その変更を正しくクライアントが適用したかは保証されない。

リクエストパラメータは textDocument/willSave と全く同じで、レスポンスの型は以下になる。

TextEdit[] | null

TextEdit 型は以下で定義される

interface TextEdit {
    range: Range;
    newText: string;
}

これはその名の通り TextDocument に適用する変更を表わすための型である。
rangestartend が等しいときは newText の挿入を意味し、newText が空のときは range 内の文字列を削除することを示す。
TextEdit[] は単純にこれをリストにしたものではあるが、リスト内の各 range が被ってはならない、というプロトコル上の制約が追加される。
ただし range の中の start が等しいことは許容される。
何をしたいかと言うと、同じ TextDocument の同じ位置への複数回の挿入を許容したいのである。 なお textDocument/didChangecontentChanges フィールドも deprecated な rangeLength を持っていることを除けばこれと同一に見えるが、TextEdit[] はあくまでそれ全体で一つの変更を示しており、contentChanges フィールドはリストの各要素が一つの変更を示しているという点で異なる。
// これは textDocument/didChange でのバージョン情報の扱いと range の被りについての制約が無いことからの推測である。

今回の textDocument/willSave textDocument/willSaveWaitUntilvim-lsp にも実装されておらず、適当に他の言語のサーバをあたってみるも実装されてるやつは見当らずだったためここまで。

次回 textDocument/didSave 予定

-- 追記(2020/08/15) -- かいた

tennashi.hatenablog.com