Skip to content

JA07_補完

HAYAMA_Kaoru edited this page Oct 11, 2023 · 1 revision

サブパッケージの completion を使って、補完機能を実装することができます。

var editor readline.Editor

editor.BindKey("\t", completion.CmdCompletionOrList {
   Completion: completion.File{},
})

補完機能には特別なフックがあるわけではなく、特定のキーにオブジェクトをアサインする形になります。

CmdCompletionOrList は補完を行うクラスですが、補完するための集合を得る処理は .Completion フィールドに設定されたクラスに外だしする形にしています。 .Completion フィールドに設定するインスタンスは次の interface を満たしている必要があります。

type Completion interface {
	Delimiters() string
	Enclosures() string
	List(fields []string) (fullnames, basenames []string)
}

Delimiters は脇役です。補完対象の中に含まれない文字、区切り文字を設定します。ファイルを補完する completion.File では次のようにファイル名に含まれない文字のリストを返すだけで済んでいます。

func (File) Delimiters() string {
	return "&|><;"
}

Enclosures も脇役です。補完対象の中に空白などが含まれている時に、全体を囲むために使ってよい文字を指定します。ファイルを補完する completion.File では次のとおりです。

func (File) Enclosures() string {
	return `"'`
}

ダブルクォーテーションとシングルクォーテーションの二つが指定されています。この場合、入力者は両方とも使用することができますが、どちらもまだ使われておらず、プログラムが最初に補完するのに必要になった場合は最初の方、つまりダブルクォーテーションが使われます。

そして List メソッドこそが主役です。このメソッドで補完候補一覧を提供してください。引数を説明します。

List(fields []string) (fullnames, basenames []string)

fields はコマンドラインに入力されたテキストで、カーソルより左にあるもののを空白で分割した文字列配列です。つまり、補完されるターゲットは fields[len(fields)-1] になります。

なぜ、すべてのフィールドが補完で与えられるかというと、文脈によって補完候補がより絞られる場合があるからです。たとえばコマンドラインシェルの場合、ls h* の場合は「hで始まるすべてのファイル」が候補となりますが、cd h* の場合は「h で始まるディレクトリ名のみ」になります。そういった判断をこれで行います。

そして戻り値ですが、二種類提供いただく必要があります。違いですが、ファイル補完の場合 foo/b を補完した場合があった時、補完すべき文字列としては foo/bar となりますが、一覧表示する場合、bar だけで OK です。前者を設定するのが fullnames であり、後者を設定するのが basenames となります。

  • fullnames と basenames の各要素は同じものの別の名前を指す形になるので、要素数は同じである必要があります。
  • basenames が nil の場合は、fullnames と同じ内容とみなされます。

一覧表示が要らない場合

CmdCompetionOrList を使うと、TABキーを二回連打した場合、補完候補を一覧表示します。この動作が不都合な場合、次のように CmdCompletion を使ってください

var editor readline.Editor

editor.BindKey("\t", completion.CmdCompletion {
   Completion: completion.File{},
})