///|
/// 保存一次已经在 MoonBit 栈上完成规范化的同步补全结果。
priv struct SessionCompletionResponse {
candidates : FixedArray[Bytes]
replacement : Bytes?
}
///|
fn normalize_session_candidates(
candidates : Array[String],
) -> Array[String] raise ReadlineError {
let normalized : Array[String] = []
for candidate in candidates {
guard !candidate.contains("\u{0}") else { raise EmbeddedNul }
if candidate != "" && !normalized.contains(candidate) {
normalized.push(candidate)
}
}
normalized
}
///|
fn session_common_prefix(left : String, right : String) -> String {
let left = left.iter().collect()
let right = right.iter().collect()
let length = left.length().min(right.length())
let common_length = for index = 0; index < length; index = index + 1 {
if left[index] != right[index] {
break index
}
} nobreak {
length
}
let result = StringBuilder()
for index in 0.. String? {
match candidates {
[] => None
[candidate] => Some(candidate)
[first, .. rest] => {
let prefix = rest.fold(init=first, session_common_prefix)
if prefix.has_prefix(current_word) &&
prefix.char_length() > current_word.char_length() {
Some(prefix)
} else {
None
}
}
}
}
///|
#warnings("-unused_value")
fn make_session_completion_response(
completer : (CompletionContext) -> Array[String],
context : CompletionContext,
) -> SessionCompletionResponse raise ReadlineError {
let candidates = normalize_session_candidates(completer(context))
let replacement = session_completion_replacement(
candidates,
context.current_word,
).map(replacement => @utf8.encode(replacement))
let encoded = candidates.map(candidate => @utf8.encode(candidate))
{ candidates: FixedArray::from_array(encoded), replacement }
}
///|
#warnings("-unused_value")
fn EditorOwner::session_completion_context(
self : EditorOwner,
generation : Int64,
) -> CompletionContext raise ReadlineError {
let before = self.session_completion_before(generation)
let after = self.session_completion_after(generation)
let word = self.session_completion_word(generation)
guard self.session_operation_status() == 0 else {
raise ReadFailed(self.session_errno())
}
let before_cursor = @utf8.decode(before) catch { _ => raise InvalidUtf8 }
let after_cursor = @utf8.decode(after) catch { _ => raise InvalidUtf8 }
let current_word = @utf8.decode(word) catch { _ => raise InvalidUtf8 }
{ before_cursor, after_cursor, current_word }
}
///|
#warnings("-unused_value")
fn EditorOwner::session_apply_completion(
self : EditorOwner,
generation : Int64,
response : SessionCompletionResponse,
) -> Unit raise ReadlineError {
let (replacement, has_replacement) = match response.replacement {
Some(replacement) => (replacement, true)
None => (Bytes::new(0), false)
}
match
self.session_resume_completion(
generation,
response.candidates,
replacement,
has_replacement,
) {
0 => ()
1 => raise Busy
2 => raise EmbeddedNul
5 => raise CleanupFailed(self.session_errno())
_ => raise ReadFailed(self.session_errno())
}
}