Files
clide/test/editor/editor_settings_test.dart
jpmschweitzerandClaude Opus 4.8 d4b39e430a route editor config through a source-agnostic EditorSettings model (T-29)
Introduce EditorSettings as the effective, source-agnostic editor configuration
the editor and save path obey. .editorconfig is demoted to one *source*
(editorconfig.dart now returns EditorSettings); editor_settings_resolver.dart is
the single composition seam where future sources — a settings panel, a clide
settings file — layer in via merge() without the editor changing.

The registry resolves settings on buffer load and, when a .editorconfig is saved
in-app, re-resolves every open buffer and emits editor.settings-changed (a hook
in save, not a filesystem watcher — the realistic case, cheaply). Buffer JSON
carries editorSettings. 100% line coverage on the new model + resolver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 09:15:49 +02:00

82 lines
3.8 KiB
Dart

import 'package:clide/src/editor/editor_settings.dart';
import 'package:test/test.dart';
void main() {
group('EditorSettings model', () {
test('toJson omits unset keys; fromJson round-trips', () {
expect(const EditorSettings().toJson(), isEmpty);
const s = EditorSettings(indentStyle: 'space', indentSize: 2, maxLineLength: 80, insertFinalNewline: true);
expect(EditorSettings.fromJson(s.toJson()).toJson(), s.toJson());
expect(EditorSettings.fromJson('not a map'), EditorSettings.empty);
});
test('isEmpty + eolString', () {
expect(EditorSettings.empty.isEmpty, isTrue);
expect(const EditorSettings(indentSize: 2).isEmpty, isFalse);
expect(const EditorSettings(endOfLine: 'crlf').eolString, '\r\n');
expect(const EditorSettings(endOfLine: 'lf').eolString, '\n');
expect(const EditorSettings(endOfLine: 'cr').eolString, '\r');
expect(EditorSettings.empty.eolString, isNull);
});
test('indentUnit reflects style + size', () {
expect(const EditorSettings(indentStyle: 'tab').indentUnit, '\t');
expect(const EditorSettings(indentStyle: 'space', indentSize: 3).indentUnit, ' ');
expect(const EditorSettings(indentStyle: 'space').indentUnit, ' '); // defaults to 4
expect(const EditorSettings(indentSize: 2).indentUnit, ' '); // size with no style → spaces
expect(EditorSettings.empty.indentUnit, isNull); // no opinion → keep default
});
test('merge — later source wins per set field, others fall through', () {
const base = EditorSettings(indentStyle: 'space', indentSize: 2, maxLineLength: 80);
const over = EditorSettings(indentSize: 4, insertFinalNewline: true);
final m = base.merge(over);
expect(m.indentSize, 4); // overridden
expect(m.indentStyle, 'space'); // inherited
expect(m.maxLineLength, 80); // inherited
expect(m.insertFinalNewline, isTrue); // added
// Merging empty over a value is a no-op; value over empty adopts it.
expect(base.merge(EditorSettings.empty).toJson(), base.toJson());
expect(EditorSettings.empty.merge(base).toJson(), base.toJson());
});
});
group('EditorSettings.applyOnSave', () {
test('trims trailing whitespace on every line, any EOL', () {
const cfg = EditorSettings(trimTrailingWhitespace: true);
expect(cfg.applyOnSave('a \nb\t\nc'), 'a\nb\nc');
expect(cfg.applyOnSave('a \r\nb \r\n'), 'a\r\nb\r\n');
});
test('inserts a final newline when missing', () {
const cfg = EditorSettings(insertFinalNewline: true);
expect(cfg.applyOnSave('abc'), 'abc\n');
expect(cfg.applyOnSave('abc\n'), 'abc\n');
});
test('insert_final_newline=false strips trailing newlines', () {
expect(const EditorSettings(insertFinalNewline: false).applyOnSave('abc\n\n'), 'abc');
});
test('final newline uses the configured EOL, else the detected one', () {
expect(const EditorSettings(endOfLine: 'crlf', insertFinalNewline: true).applyOnSave('abc'), 'abc\r\n');
expect(const EditorSettings(insertFinalNewline: true).applyOnSave('a\r\nb'), 'a\r\nb\r\n');
});
test('normalizes EOL across the whole file', () {
expect(const EditorSettings(endOfLine: 'crlf').applyOnSave('a\nb\nc'), 'a\r\nb\r\nc');
expect(const EditorSettings(endOfLine: 'lf').applyOnSave('a\r\nb\r\n'), 'a\nb\n');
});
test('no opinion (and empty content) leaves the text untouched', () {
expect(EditorSettings.empty.applyOnSave('a \r\nb'), 'a \r\nb');
expect(const EditorSettings(insertFinalNewline: true).applyOnSave(''), '');
});
test('composes trim + EOL + final newline in order', () {
const cfg = EditorSettings(trimTrailingWhitespace: true, endOfLine: 'lf', insertFinalNewline: true);
expect(cfg.applyOnSave('a \r\nb\t'), 'a\nb\n');
});
});
}