droid_wrap/android/text.rs
1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14use crate::{
15 JObjNew, JObjRef, JProxy, JType, Result,
16 java::lang::{CharSequence, CharSequenceImpl},
17 java_class, java_implement, java_interface, java_method,
18};
19use std::sync::Arc;
20
21/**
22整数的位定义,定义可编辑对象中保存的文本的基本内容类型。支持的类可以与变体和标志组合以指示所需的行为。
23
24示例
25- 密码字段,密码对用户可见: inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
26- 多行邮箱地址,自动大写: inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_POSTAL_ADDRESS | TYPE_TEXT_FLAG_MULTI_LINE
27- 时间字段: inputType = TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_TIME
28*/
29#[java_interface(name = "android/text/InputType")]
30pub trait InputType {
31 /// 确定给定文本总体类别的位掩码。当前支持的类别有:TYPE_CLASS_TEXT、TYPE_CLASS_NUMBER、TYPE_CLASS_PHONE、TYPE_CLASS_DATETIME。IME 作者:如果您不了解该类别,则假定 TYPE_CLASS_TEXT 不带任何变体或标志。
32 const TYPE_MASK_CLASS: i32 = 0x0000000f;
33
34 /// 确定基础内容类的变化的位掩码。
35 const TYPE_MASK_VARIATION: i32 = 0x00000ff0;
36
37 /// 提供选项的附加位标志的位掩码。
38 const TYPE_MASK_FLAGS: i32 = 0x00fff000;
39
40 /// 未指定显式类型时的特殊内容类型。这应解释为目标输入连接不够丰富,无法处理和显示候选文本之类的内容,也无法检索当前文本,因此输入法将需要在有限的“生成按键事件”模式下运行(如果支持)。
41 /// 请注意,某些输入法可能不支持该模式,例如,即使设置了此标志,基于语音的输入法也可能无法生成按键事件。
42 const TYPE_NULL: i32 = 0x00000000;
43
44 /// 普通文本类。该类支持以下标志(只能设置其中一个):TYPE_TEXT_FLAG_CAP_CHARACTERS、TYPE_TEXT_FLAG_CAP_WORDS 和 TYPE_TEXT_FLAG_CAP_SENTENCES。
45 /// 它还支持以下变体:TYPE_TEXT_VARIATION_NORMAL 和 TYPE_TEXT_VARIATION_URI。如果您无法识别变体,则应假定为普通。
46 const TYPE_CLASS_TEXT: i32 = 0x00000001;
47
48 /// TYPE_CLASS_TEXT 的标志:将所有字符大写。覆盖 TYPE_TEXT_FLAG_CAP_WORDS 和 TYPE_TEXT_FLAG_CAP_SENTENCES。此值明确定义为与 TextUtils#CAP_MODE_CHARACTERS 相同。当然,这只影响有大写和小写字母的语言。
49 const TYPE_TEXT_FLAG_CAP_CHARACTERS: i32 = 0x00001000;
50
51 /// TYPE_CLASS_TEXT 的标志:将每个单词的第一个字符大写。覆盖 TYPE_TEXT_FLAG_CAP_SENTENCES。此值明确定义为与 TextUtils#CAP_MODE_WORDS 相同。当然,这只影响有大写和小写字母的语言。
52 const TYPE_TEXT_FLAG_CAP_WORDS: i32 = 0x00002000;
53
54 /// TYPE_CLASS_TEXT 的标志:将每个句子的第一个字符大写。此值明确定义为与 TextUtils#CAP_MODE_SENTENCES 相同。例如,在英语中,它意味着在句号和空格后大写(请注意,其他语言可能对句号有不同的字符,或者不使用空格,或者使用不同的语法规则)。当然,这只影响有大写和小写字母的语言。
55 const TYPE_TEXT_FLAG_CAP_SENTENCES: i32 = 0x00004000;
56
57 /// TYPE_CLASS_TEXT 的标志:用户正在输入自由格式的文本,该文本应应用自动更正。如果没有此标志,IME 将不会尝试更正拼写错误。除非您真的希望用户在此字段中输入非单词,例如为游戏中的角色选择名称,否则您应该始终设置此标志。与 TYPE_TEXT_FLAG_AUTO_COMPLETE 和 TYPE_TEXT_FLAG_NO_SUGGESTIONS 形成对比:`` 表示 IME 将在用户输入时尝试自动更正拼写错误,但未定义 IME 是否提供显示建议的界面。
58 const TYPE_TEXT_FLAG_AUTO_CORRECT: i32 = 0x00008000;
59
60 //noinspection SpellCheckingInspection
61 /// TYPE_CLASS_TEXT 的标志:文本编辑器(即应用程序)正在根据其自身的语义自动完成输入的文本,并在用户输入时将其呈现给用户。这通常意味着输入法不应显示候选词,但可以期望编辑器从 android.view.inputmethod.InputMethodSession#displayCompletions InputMethodSession.displayCompletions() 提供自己的完成/候选词,这是编辑器调用 android.view.inputmethod.InputMethodManager#displayCompletions InputMethodManager.displayCompletions() 的结果。
62 /// 请注意与 TYPE_TEXT_FLAG_AUTO_CORRECT 和 TYPE_TEXT_FLAG_NO_SUGGESTIONS 的对比:`` 表示编辑器应显示一个用于显示建议的界面,但它不提供自己的界面,而是依靠编辑器传递完成/更正。
63 const TYPE_TEXT_FLAG_AUTO_COMPLETE: i32 = 0x00010000;
64
65 /// TYPE_CLASS_TEXT 的标志:可在字段中输入多行文本。如果未设置此标志,则文本字段将限制为一行。当未设置此标志时,IME 还可以选择不显示回车键,因为不需要创建新行。
66 const TYPE_TEXT_FLAG_MULTI_LINE: i32 = 0x00020000;
67
68 /// TYPE_CLASS_TEXT 标志:与此相关的常规文本视图不应该是多行,但是当全屏输入法提供文本时,如果可以的话它应该使用多行。
69 const TYPE_TEXT_FLAG_IME_MULTI_LINE: i32 = 0x00040000;
70
71 /// TYPE_CLASS_TEXT 的标志:输入法不需要显示任何基于字典的候选项。这对于不包含该语言单词且不会从任何基于字典的 补 全 或 更正 中受益的文本视图很有用。设置后,它会覆盖 TYPE_TEXT_FLAG_AUTO_CORRECT 值。请避免使用此选项,除非您确定这是您想要的。许多输入法需要建议才能正常工作,例如基于手势输入的建议。如果您只是不想让 IME 更正拼写错误,请考虑清除 TYPE_TEXT_FLAG_AUTO_CORRECT。
72 /// 请注意与 TYPE_TEXT_FLAG_AUTO_CORRECT 和 TYPE_TEXT_FLAG_AUTO_COMPLETE 的对比:` ` 表示 IME 不需要显示界面来显示建议。大多数 IME 也会认为这意味着它们不需要尝试自动更正用户正在输入的内容。
73 const TYPE_TEXT_FLAG_NO_SUGGESTIONS: i32 = 0x00080000;
74
75 /// TYPE_CLASS_TEXT 标志:让 IME 知道应用程序需要文本转换建议。文本转换建议适用于具有发音字符和目标字符的音译语言。当用户输入发音字符时,IME 可以向用户提供可能的目标字符。设置此标志后,IME 应通过 Builder#setTextConversionSuggestions(List) 插入文本转换建议,并且 IME 将使用文本转换建议初始化的 TextAttribute 提供给应用程序。要接收附加信息,应用程序需要实现 InputConnection#setComposingText(CharSequence, int, TextAttribute)、InputConnection#setComposingRegion(int, int, TextAttribute) 和 InputConnection#commitText(CharSequence, int, TextAttribute)。
76 const TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS: i32 = 0x00100000;
77
78 /// TYPE_CLASS_TEXT 的默认变体:普通的旧文本。
79 const TYPE_TEXT_VARIATION_NORMAL: i32 = 0x00000000;
80
81 /// type_class_text的变体:输入URI。
82 const TYPE_TEXT_VARIATION_URI: i32 = 0x00000010;
83
84 /// TYPE_CLASS_TEXT 的变体:输入电子邮件地址。
85 const TYPE_TEXT_VARIATION_EMAIL_ADDRESS: i32 = 0x00000020;
86
87 /// TYPE_CLASS_TEXT 的变体:输入电子邮件的主题行。
88 const TYPE_TEXT_VARIATION_EMAIL_SUBJECT: i32 = 0x00000030;
89
90 /// TYPE_CLASS_TEXT 的变体:输入简短、可能非正式的消息,例如即时消息或文本消息。
91 const TYPE_TEXT_VARIATION_SHORT_MESSAGE: i32 = 0x00000040;
92
93 /// TYPE_CLASS_TEXT 的变体:输入较长的、可能正式的消息的内容,例如电子邮件的正文。
94 const TYPE_TEXT_VARIATION_LONG_MESSAGE: i32 = 0x00000050;
95
96 /// TYPE_CLASS_TEXT 的变体:输入一个人的姓名。
97 const TYPE_TEXT_VARIATION_PERSON_NAME: i32 = 0x00000060;
98
99 /// TYPE_CLASS_TEXT 的变体:输入邮寄地址。
100 const TYPE_TEXT_VARIATION_POSTAL_ADDRESS: i32 = 0x00000070;
101
102 /// TYPE_CLASS_TEXT 的变体:输入密码。
103 const TYPE_TEXT_VARIATION_PASSWORD: i32 = 0x00000080;
104
105 /// TYPE_CLASS_TEXT 的变体:输入密码,该密码应该对用户可见。
106 const TYPE_TEXT_VARIATION_VISIBLE_PASSWORD: i32 = 0x00000090;
107
108 /// TYPE_CLASS_TEXT 的变体:在网络表单内输入文本。
109 const TYPE_TEXT_VARIATION_WEB_EDIT_TEXT: i32 = 0x000000a0;
110
111 /// TYPE_CLASS_TEXT 的变体:输入文本来过滤列表的内容等。
112 const TYPE_TEXT_VARIATION_FILTER: i32 = 0x000000b0;
113
114 /// TYPE_CLASS_TEXT 的变体:输入语音发音的文本,例如联系人中的语音姓名字段。这最适用于一种拼写可能有多种语音读法的语言,例如日语。
115 const TYPE_TEXT_VARIATION_PHONETIC: i32 = 0x000000c0;
116
117 //noinspection SpellCheckingInspection
118 /// TYPE_CLASS_TEXT 的变体:在 Web 表单中输入电子邮件地址。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;如果不是,则当通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被视为 TYPE_TEXT_VARIATION_EMAIL_ADDRESS。
119 const TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS: i32 = 0x000000d0;
120
121 //noinspection SpellCheckingInspection
122 /// TYPE_CLASS_TEXT 的变体:在 Web 表单中输入密码。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;如果不是,则当通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被视为 TYPE_TEXT_VARIATION_PASSWORD。
123 const TYPE_TEXT_VARIATION_WEB_PASSWORD: i32 = 0x000000e0;
124
125 /// 数字文本类。此类支持以下标志:TYPE_NUMBER_FLAG_SIGNED 和 TYPE_NUMBER_FLAG_DECIMAL。它还支持以下变体:TYPE_NUMBER_VARIATION_NORMAL 和 TYPE_NUMBER_VARIATION_PASSWORD。IME 作者:如果您无法识别变体,则应假定为正常。
126 const TYPE_CLASS_NUMBER: i32 = 0x00000002;
127
128 /// TYPE_CLASS_NUMBER 标志:该数字是有符号的,允许在开头使用正号或负号。
129 const TYPE_NUMBER_FLAG_SIGNED: i32 = 0x00001000;
130
131 /// TYPE_CLASS_NUMBER 标志:数字是十进制,允许小数点提供分数值。
132 const TYPE_NUMBER_FLAG_DECIMAL: i32 = 0x00002000;
133
134 //noinspection SpellCheckingInspection
135 /// TYPE_CLASS_NUMBER 的默认变体:普通数字文本。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;否则,在通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被丢弃。
136 const TYPE_NUMBER_VARIATION_NORMAL: i32 = 0x00000000;
137
138 //noinspection SpellCheckingInspection
139 /// TYPE_CLASS_NUMBER 的变体:输入数字密码。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;否则,在通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被丢弃。
140 const TYPE_NUMBER_VARIATION_PASSWORD: i32 = 0x00000010;
141
142 /// 电话号码类。此类目前不支持任何变体或标志。
143 const TYPE_CLASS_PHONE: i32 = 0x00000003;
144
145 /// 日期和时间类。它支持以下变体:TYPE_DATETIME_VARIATION_NORMAL TYPE_DATETIME_VARIATION_DATE 和 TYPE_DATETIME_VARIATION_TIME。
146 const TYPE_CLASS_DATETIME: i32 = 0x00000004;
147
148 /// TYPE_CLASS_DATETIME 的默认变体:允许输入日期和时间。
149 const TYPE_DATETIME_VARIATION_NORMAL: i32 = 0x00000000;
150
151 /// TYPE_CLASS_DATETIME 的默认变体:仅允许输入日期。
152 const TYPE_DATETIME_VARIATION_DATE: i32 = 0x00000010;
153
154 /// TYPE_CLASS_DATETIME 的默认变体:只允许输入时间。
155 const TYPE_DATETIME_VARIATION_TIME: i32 = 0x00000020;
156}
157
158#[doc(hidden)]
159#[java_class(name = "android/text/InputTypeImpl")]
160pub struct InputTypeImpl;
161
162impl InputType for InputTypeImpl {}
163
164/**
165这是文本的接口,其内容和标记可以更改(与字符串等不可变文本相反)。如果您创建可编辑的 DynamicLayout,则布局将随着文本的更改而重新排列。
166*/
167#[java_interface(name = "android/text/Editable")]
168pub trait Editable: CharSequence {
169 #[doc(hidden)]
170 type Cs: CharSequence;
171 #[doc(hidden)]
172 type E: Editable;
173
174 /**
175 用源切片 start…end 的副本替换此 Editable 中指定范围 (st…en) 的文本。目标切片可能为空,在这种情况下操作为插入;源切片可能为空,在这种情况下操作为删除。
176 在提交更改之前,使用 setFilters 设置的每个过滤器都有机会修改源文本。如果源是 Spanned,则来自源的跨度将保留到 Editable 中。Editable 中完全覆盖替换范围的现有跨度将被保留,但严格在替换范围内的任何跨度将被删除。
177 如果源包含带有 Spanned.SPAN_PARAGRAPH 标志的跨度,并且它不满足段落边界约束,则不会保留它。作为特殊情况,即使替换了光标所在的整个范围,光标位置也会保留。
178 返回:对此对象的引用。
179 */
180 fn replace(&self, st: i32, en: i32, source: Self::Cs, start: i32, end: i32) -> Self::E;
181
182 /**
183 方便 replace(st, en, text, 0, text.length())
184 */
185 fn replace_convenience(&self, st: i32, en: i32, text: Self::Cs) -> Self::E;
186
187 /**
188 方便 replace(where, where, text, start, end)
189 */
190 fn insert(&self, r#where: i32, text: Self::Cs, start: i32, end: i32) -> Self::E;
191
192 /**
193 方便 replace(where, where, text, 0, text.length());
194 */
195 fn insert_convenience(&self, r#where: i32, text: Self::Cs) -> Self::E;
196
197 /**
198 方便 replace(st, en, "", 0, 0)
199 */
200 fn delete(&self, st: i32, en: i32) -> Self::E;
201
202 /// 方便 replace(length(), length(), text, 0, text.length())
203 fn append_convenience(&self, text: Self::Cs) -> Self::E;
204
205 /// 方便 replace(length(), length(), text, start, end)
206 fn append(&self, text: Self::Cs, start: i32, end: i32) -> Self::E;
207
208 /// 方便 append(String.valueOf(text))
209 fn append_char(&self, text: char) -> Self::E;
210
211 /// 方便 replace(0, length(), "", 0, 0).
212 /// 请注意,这将清除文本,而不是跨度;如果需要,请使用 clearSpans。
213 fn clear(&self);
214
215 /// 从可编辑对象中移除所有跨度,就像在每个跨度上调用 removeSpan 一样。
216 fn clear_spans(&self);
217}
218
219#[doc(hidden)]
220#[java_class(name = "android/text/EditableImpl", extends=CharSequenceImpl)]
221pub struct EditableImpl;
222
223impl CharSequence for EditableImpl {
224 fn length(&self) -> i32 {
225 self._based.length()
226 }
227
228 fn char_at(&self, index: i32) -> Result<char> {
229 self._based.char_at(index)
230 }
231}
232
233#[java_implement]
234impl Editable for EditableImpl {
235 type Cs = CharSequenceImpl;
236 type E = Self;
237
238 #[java_method(type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
239 fn replace(
240 &self,
241 st: i32,
242 en: i32,
243 source: <Self as Editable>::Cs,
244 start: i32,
245 end: i32,
246 ) -> Self::E {
247 }
248
249 #[java_method(overload=replace, type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
250 fn replace_convenience(&self, st: i32, en: i32, text: <Self as Editable>::Cs) -> Self::E {}
251
252 #[java_method(type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
253 fn insert(&self, r#where: i32, text: <Self as Editable>::Cs, start: i32, end: i32) -> Self::E {}
254
255 #[java_method(overload=insert, type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
256 fn insert_convenience(&self, r#where: i32, text: <Self as Editable>::Cs) -> Self::E {}
257
258 #[java_method(type_bound=(Self::E, Editable))]
259 fn delete(&self, st: i32, en: i32) -> Self::E {}
260
261 #[java_method(overload=append, type_bound=(<Self as Editable>::Cs, CharSequence),type_bound=(Self::E, Editable))]
262 fn append_convenience(&self, text: <Self as Editable>::Cs) -> Self::E {}
263
264 #[java_method(type_bound=(<Self as Editable>::Cs, CharSequence),type_bound=(Self::E, Editable))]
265 fn append(&self, text: <Self as Editable>::Cs, start: i32, end: i32) -> Self::E {}
266
267 #[java_method(type_bound=(Self::E, Editable))]
268 fn append_char(&self, text: char) -> Self::E {}
269
270 #[java_method]
271 fn clear(&self) {}
272
273 #[java_method]
274 fn clear_spans(&self) {}
275}
276
277/**
278当此类型的对象附加到 Editable 时,其方法将在文本改变时被调用。
279*/
280#[java_interface(name = "android/text/TextWatcher")]
281pub trait TextWatcher {
282 #[doc(hidden)]
283 type Cs: CharSequence;
284 #[doc(hidden)]
285 type E: Editable;
286
287 /// 调用此方法是为了通知您,在 s 中,从 start 开始的 count 个字符即将被长度为 after 的新文本替换。
288 /// 尝试从此回调更改 s 是错误的。
289 fn before_text_changed(&self, s: Self::Cs, start: i32, count: i32, after: i32);
290
291 /// 调用此方法是为了通知您,在 s 中,从 start 开始的 count 个字符刚刚替换了之前长度为 before 的旧文本。
292 /// 尝试通过此回调更改 s 是错误的。
293 fn on_text_changed(&self, s: Self::Cs, start: i32, before: i32, count: i32);
294
295 //noinspection SpellCheckingInspection
296 /// 调用此方法是为了通知您,在 s 中的某个地方,文本已发生更改。从此回调对 s 进行进一步更改是合法的,但请注意不要陷入无限循环,因为您所做的任何更改都会导致此方法再次递归调用。
297 /// (您不会被告知更改发生的位置,因为其他 afterTextChanged() 方法可能已经进行了其他更改并使偏移量无效。但如果您需要在此处知道,您可以在 onTextChanged 中使用 Spannable.setSpan 来标记您的位置,然后从此处查找 span 结束的位置。
298 fn after_text_changed(&self, s: Self::E);
299}
300
301#[doc(hidden)]
302#[java_class(name = "android/text/TextWatcherImpl")]
303pub struct TextWatcherImpl {
304 before_text_changed:
305 Box<dyn Fn(<TextWatcherImpl as TextWatcher>::Cs, i32, i32, i32) + Send + Sync>,
306 on_text_changed: Box<dyn Fn(<TextWatcherImpl as TextWatcher>::Cs, i32, i32, i32) + Send + Sync>,
307 after_text_changed: Box<dyn Fn(<TextWatcherImpl as TextWatcher>::E) + Send + Sync>,
308}
309
310impl TextWatcherImpl {
311 pub fn from_fn(
312 before_text_changed: impl Fn(
313 /* s */ <Self as TextWatcher>::Cs,
314 /* start */ i32,
315 /* count */ i32,
316 /* after */ i32,
317 ) + Send
318 + Sync
319 + 'static,
320 on_text_changed: impl Fn(
321 /* s */ <Self as TextWatcher>::Cs,
322 /* start */ i32,
323 /* before */ i32,
324 /* count */ i32,
325 ) + Send
326 + Sync
327 + 'static,
328 after_text_changed: impl Fn(/* s */ <Self as TextWatcher>::E) + Send + Sync + 'static,
329 ) -> Result<Arc<Self>> {
330 Self::new(TextWatcherImplDefault {
331 before_text_changed: Box::new(before_text_changed),
332 on_text_changed: Box::new(on_text_changed),
333 after_text_changed: Box::new(after_text_changed),
334 })
335 }
336}
337
338impl Default for TextWatcherImplDefault {
339 fn default() -> Self {
340 Self {
341 before_text_changed: Box::new(|_, _, _, _| ()),
342 on_text_changed: Box::new(|_, _, _, _| ()),
343 after_text_changed: Box::new(|_| ()),
344 }
345 }
346}
347
348#[java_implement]
349impl TextWatcher for TextWatcherImpl {
350 type Cs = CharSequenceImpl;
351 type E = EditableImpl;
352
353 fn before_text_changed(
354 &self,
355 s: <Self as TextWatcher>::Cs,
356 start: i32,
357 count: i32,
358 after: i32,
359 ) {
360 (self.before_text_changed)(s, start, count, after)
361 }
362
363 fn on_text_changed(&self, s: <Self as TextWatcher>::Cs, start: i32, before: i32, count: i32) {
364 (self.on_text_changed)(s, start, before, count)
365 }
366
367 fn after_text_changed(&self, s: <Self as TextWatcher>::E) {
368 (self.after_text_changed)(s)
369 }
370}
371
372/// 测试android.text
373#[cfg(feature = "test_android_text")]
374pub fn test() {
375 let watcher = TextWatcherImpl::from_fn(
376 |_s, _start, _count, _after| (),
377 |_s, _start, _before, _count| (),
378 |_s| (),
379 )
380 .unwrap();
381 dbg!(watcher);
382}