editor-core 0.4.1

A headless editor engine focused on state management, Unicode-aware text measurement, and coordinate conversion.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
//! End-to-end integration tests
//!
//! Tests the full editor workflow.

use editor_core::{
    Command, CommandExecutor, CursorCommand, EditCommand, EditorStateManager, Position,
    StyleCommand, ViewCommand,
};

/// Test a full editing session.
#[test]
fn test_full_editing_session() {
    println!("测试完整编辑会话...");

    // 1. 创建编辑器
    let mut executor = CommandExecutor::empty(80);

    // 2. 插入初始内容
    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 0,
            text: "fn main() {\n    println!(\"Hello\");\n}\n".to_string(),
        }))
        .unwrap();

    assert_eq!(executor.editor().line_count(), 4);
    assert!(executor.editor().get_text().contains("Hello"));

    // 3. 移动光标到第二行
    executor
        .execute(Command::Cursor(CursorCommand::MoveTo {
            line: 1,
            column: 4,
        }))
        .unwrap();

    assert_eq!(executor.editor().cursor_position(), Position::new(1, 4));

    // 4. 设置选择范围
    executor
        .execute(Command::Cursor(CursorCommand::SetSelection {
            start: Position::new(1, 4),
            end: Position::new(1, 27),
        }))
        .unwrap();

    assert!(executor.editor().selection().is_some());

    // 5. 替换选中文本
    executor
        .execute(Command::Edit(EditCommand::Replace {
            start: 8,   // "fn main() {\n    " 之后
            length: 19, // println!("Hello");
            text: "println!(\"World\");".to_string(),
        }))
        .unwrap();

    assert!(executor.editor().get_text().contains("World"));
    assert!(!executor.editor().get_text().contains("Hello"));

    // 6. 添加样式
    executor
        .execute(Command::Style(StyleCommand::AddStyle {
            start: 0,
            end: 2,
            style_id: 1, // 关键字样式
        }))
        .unwrap();

    // 7. 获取视口
    let result = executor.execute(Command::View(ViewCommand::GetViewport {
        start_row: 0,
        count: 10,
    }));

    assert!(result.is_ok());

    println!("✓ 完整编辑会话测试通过");
}

/// Test state management integration.
#[test]
fn test_state_management_integration() {
    println!("测试状态管理集成...");

    let mut manager = EditorStateManager::new("Initial text", 80);

    // 记录初始状态
    let initial_version = manager.version();
    let initial_state = manager.get_full_state();

    assert_eq!(initial_state.document.line_count, 1);
    assert!(!initial_state.document.is_modified);

    // 修改文档
    manager
        .execute(Command::Edit(EditCommand::Insert {
            offset: 0,
            text: "New: ".to_string(),
        }))
        .unwrap();

    // 验证状态变更
    assert!(manager.version() > initial_version);
    assert!(manager.has_changed_since(initial_version));
    assert!(manager.get_document_state().is_modified);

    // 保存文档
    manager.mark_saved();
    assert!(!manager.get_document_state().is_modified);

    println!("✓ 状态管理集成测试通过");
}

/// Test multi-cursor editing scenario (simulated).
#[test]
fn test_multi_cursor_scenario() {
    println!("测试多光标编辑场景...");

    let mut executor = CommandExecutor::new("line1\nline2\nline3\n", 80);

    // 在每行开头插入行号
    // 第1行
    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 0,
            text: "1: ".to_string(),
        }))
        .unwrap();

    // 第2行 (offset需要调整,因为前面插入了3个字符)
    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 9, // "1: line1\n" = 9
            text: "2: ".to_string(),
        }))
        .unwrap();

    // 第3行
    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 18, // "1: line1\n2: line2\n" = 18
            text: "3: ".to_string(),
        }))
        .unwrap();

    let text = executor.editor().get_text();
    assert!(text.contains("1: line1"));
    assert!(text.contains("2: line2"));
    assert!(text.contains("3: line3"));

    println!("✓ 多光标编辑场景测试通过");
}

/// Test large file performance.
#[test]
fn test_large_file_performance() {
    println!("测试大文件性能...");

    use std::time::Instant;

    // 创建一个中等大小的文档(1000行)
    let mut lines = Vec::new();
    for i in 0..1000 {
        lines.push(format!("Line {} with some content to make it realistic", i));
    }
    let text = lines.join("\n");

    // 测试加载性能
    let start = Instant::now();
    let mut executor = CommandExecutor::new(&text, 80);
    let load_time = start.elapsed();

    println!("  加载1000行耗时: {:?}", load_time);
    assert!(load_time.as_millis() < 100, "加载时间过长");

    // 测试插入性能
    let start = Instant::now();
    for i in 0..100 {
        let offset = i * 50; // 分散插入
        executor
            .execute(Command::Edit(EditCommand::Insert {
                offset: offset.min(executor.editor().char_count()),
                text: "X".to_string(),
            }))
            .unwrap();
    }
    let insert_time = start.elapsed();

    println!("  100次插入耗时: {:?}", insert_time);
    assert!(insert_time.as_millis() < 100, "插入时间过长");

    // 测试行访问性能
    let start = Instant::now();
    for _ in 0..1000 {
        let _ = executor.editor().line_count();
    }
    let access_time = start.elapsed();

    println!("  1000次行访问耗时: {:?}", access_time);
    assert!(access_time.as_millis() < 10, "访问时间过长");

    println!("✓ 大文件性能测试通过");
}

/// Test Unicode handling.
#[test]
fn test_unicode_handling() {
    println!("测试Unicode处理...");

    let mut executor = CommandExecutor::new("Hello 世界 👋\nこんにちは\n🎉🎊🎈", 80);

    // 验证行数正确
    assert_eq!(executor.editor().line_count(), 3);

    // 在Unicode字符中插入
    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 6, // "Hello " 之后
            text: "美丽的".to_string(),
        }))
        .unwrap();

    let text = executor.editor().get_text();
    assert!(text.contains("Hello 美丽的世界"));

    // 删除emoji
    executor
        .execute(Command::Edit(EditCommand::Delete {
            start: text.find('👋').unwrap(),
            length: 1,
        }))
        .unwrap();

    println!("✓ Unicode处理测试通过");
}

/// Test error recovery.
#[test]
fn test_error_recovery() {
    println!("测试错误恢复...");

    let mut executor = CommandExecutor::new("Test", 80);

    // 尝试无效操作
    let result = executor.execute(Command::Edit(EditCommand::Insert {
        offset: 1000,
        text: "X".to_string(),
    }));

    assert!(result.is_err());

    // 验证编辑器仍然可用
    let result = executor.execute(Command::Edit(EditCommand::Insert {
        offset: 4,
        text: " OK".to_string(),
    }));

    assert!(result.is_ok());
    assert_eq!(executor.editor().get_text(), "Test OK");

    println!("✓ 错误恢复测试通过");
}

/// Test command history.
#[test]
fn test_command_history() {
    println!("测试命令历史...");

    let mut executor = CommandExecutor::empty(80);

    // 执行一系列命令
    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 0,
            text: "A".to_string(),
        }))
        .unwrap();

    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 1,
            text: "B".to_string(),
        }))
        .unwrap();

    executor
        .execute(Command::Edit(EditCommand::Insert {
            offset: 2,
            text: "C".to_string(),
        }))
        .unwrap();

    // 验证历史记录
    assert_eq!(executor.get_command_history().len(), 3);
    assert_eq!(executor.editor().get_text(), "ABC");

    println!("✓ 命令历史测试通过");
}

/// Test style and folding integration.
#[test]
fn test_styles_and_folding() {
    println!("测试样式和折叠集成...");

    let mut manager =
        EditorStateManager::new("fn main() {\n    code();\n    more_code();\n}\n", 80);

    // 添加样式
    manager
        .execute(Command::Style(StyleCommand::AddStyle {
            start: 0,
            end: 2,
            style_id: 1,
        }))
        .unwrap();

    // 添加折叠区域
    manager
        .execute(Command::Style(StyleCommand::Fold {
            start_line: 1,
            end_line: 2,
        }))
        .unwrap();

    // 验证状态
    let folding_state = manager.get_folding_state();
    assert_eq!(folding_state.regions.len(), 1);
    assert_eq!(folding_state.collapsed_line_count, 1);

    let style_state = manager.get_style_state();
    assert_eq!(style_state.style_count, 1);

    // 查询样式
    let styles = manager.get_styles_at(0);
    assert_eq!(styles.len(), 1);
    assert_eq!(styles[0], 1);

    println!("✓ 样式和折叠集成测试通过");
}

/// Test batch command execution.
#[test]
fn test_batch_commands() {
    println!("测试批量命令执行...");

    let mut executor = CommandExecutor::empty(80);

    let commands = vec![
        Command::Edit(EditCommand::Insert {
            offset: 0,
            text: "Line 1\n".to_string(),
        }),
        Command::Edit(EditCommand::Insert {
            offset: 7,
            text: "Line 2\n".to_string(),
        }),
        Command::Edit(EditCommand::Insert {
            offset: 14,
            text: "Line 3\n".to_string(),
        }),
        Command::Cursor(CursorCommand::MoveTo { line: 1, column: 0 }),
    ];

    let results = executor.execute_batch(commands);
    assert!(results.is_ok());

    assert_eq!(executor.editor().line_count(), 4);
    assert_eq!(executor.editor().cursor_position(), Position::new(1, 0));

    println!("✓ 批量命令执行测试通过");
}

/// Test viewport management.
#[test]
fn test_viewport_management() {
    println!("测试视口管理...");

    let mut manager = EditorStateManager::new(
        &(0..100)
            .map(|i| format!("Line {}", i))
            .collect::<Vec<_>>()
            .join("\n"),
        80,
    );

    manager.set_viewport_height(20);
    manager.set_scroll_top(0);

    let viewport = manager.get_viewport_state();
    assert_eq!(viewport.visible_lines, 0..20);

    // 滚动到中间
    manager.set_scroll_top(40);
    let viewport = manager.get_viewport_state();
    assert_eq!(viewport.visible_lines, 40..60);

    // 获取视口内容
    let content = manager.get_viewport_content(40, 20);
    assert!(content.actual_line_count() <= 20);

    println!("✓ 视口管理测试通过");
}