Skip to content
Open
3 changes: 3 additions & 0 deletions example/lib/03.change_language_theme/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import 'package:highlight/languages/go.dart';
import 'package:highlight/languages/java.dart';
import 'package:highlight/languages/python.dart';
import 'package:highlight/languages/scala.dart';
import 'package:highlight/languages/yaml.dart';

final builtinLanguages = {
'dart': dart,
'go': go,
'java': java,
'python': python,
'scala': scala,
'yaml': yaml,
};

const languageList = <String>[
Expand All @@ -18,6 +20,7 @@ const languageList = <String>[
'java',
'python',
'scala',
'yaml',
];

const themeList = <String>[
Expand Down
7 changes: 7 additions & 0 deletions lib/src/code_field/code_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:highlight/highlight_core.dart';
import '../../flutter_code_editor.dart';
import '../autocomplete/autocompleter.dart';
import '../code/code_edit_result.dart';
import '../code_modifiers/paired_symbols.dart';
import '../history/code_history_controller.dart';
import '../history/code_history_record.dart';
import '../single_line_comments/parser/single_line_comments.dart';
Expand Down Expand Up @@ -124,6 +125,12 @@ class CodeController extends TextEditingController {
IndentModifier(),
CloseBlockModifier(),
TabModifier(),
PairedSymbolsCodeModifier(openChar: '(', closeString: ')'),
PairedSymbolsCodeModifier(openChar: '{', closeString: '}'),
PairedSymbolsCodeModifier(openChar: '[', closeString: ']'),
PairedSymbolsCodeModifier(openChar: '"', closeString: '"'),
PairedSymbolsCodeModifier(openChar: '`', closeString: '`'),
PairedSymbolsCodeModifier(openChar: '\'', closeString: '\''),
],
}) : _analyzer = analyzer,
_readOnlySectionNames = readOnlySectionNames,
Expand Down
30 changes: 30 additions & 0 deletions lib/src/code_modifiers/paired_symbols.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:flutter/services.dart';

import '../code_field/editor_params.dart';
import 'code_modifier.dart';

class PairedSymbolsCodeModifier extends CodeModifier {
final String openChar;
final String closeString;

const PairedSymbolsCodeModifier({
required this.openChar,
required this.closeString,
}) : super(openChar);

@override
TextEditingValue? updateString(
String text,
TextSelection sel,
EditorParams params,
) {
final replaced = replace(text, sel.start, sel.end, '$openChar$closeString');

return replaced.copyWith(
selection: TextSelection(
baseOffset: replaced.selection.baseOffset - closeString.length,
extentOffset: replaced.selection.extentOffset - closeString.length,
),
);
}
}
7 changes: 7 additions & 0 deletions lib/src/folding/parsers/parser_factory.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:highlight/highlight_core.dart';
import 'package:highlight/languages/java.dart';
import 'package:highlight/languages/python.dart';
import 'package:highlight/languages/yaml.dart';

import 'abstract.dart';
import 'highlight.dart';
import 'indent.dart';
import 'java.dart';
import 'python.dart';

Expand All @@ -15,6 +17,11 @@ class FoldableBlockParserFactory {
if (mode == java) {
return JavaFoldableBlockParser();
}

if (mode == yaml) {
return IndentFoldableBlockParser();
}

return HighlightFoldableBlockParser();
}
}
167 changes: 167 additions & 0 deletions test/src/code_modifiers/paired_symbols_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import 'package:flutter/material.dart';
import 'package:flutter_code_editor/flutter_code_editor.dart';
import 'package:flutter_code_editor/src/code_modifiers/paired_symbols.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test('Paired symbols modifier test', () {
const examples = [
//
_Example(
'Add paired symbols at the start of the string',
initialValue: TextEditingValue(
text: 'dict',
// \ cursor
selection: TextSelection.collapsed(offset: 0),
),
expected: TextEditingValue(
text: '{}dict',
// \ cursor
selection: TextSelection.collapsed(offset: 1),
),
openChar: '{',
closeString: '}',
),

_Example(
'Add paired symbols in the middle of the string',
initialValue: TextEditingValue(
text: 'print',
// \ cursor
selection: TextSelection.collapsed(offset: 3),
),
expected: TextEditingValue(
text: 'pri()nt',
// \ cursor
selection: TextSelection.collapsed(offset: 4),
),
openChar: '(',
closeString: ')',
),

_Example(
'Add paired symbols at the end of the string',
initialValue: TextEditingValue(
text: 'print',
// \ cursor
selection: TextSelection.collapsed(offset: 5),
),
expected: TextEditingValue(
text: 'print[]',
// \ cursor
selection: TextSelection.collapsed(offset: 6),
),
openChar: '[',
closeString: ']',
),

_Example(
'Add paired symbols with several close chars',
initialValue: TextEditingValue(
text: 'string',
// \ cursor
selection: TextSelection.collapsed(offset: 6),
),
expected: TextEditingValue(
text: 'string123',
// \ cursor
selection: TextSelection.collapsed(offset: 7),
),
openChar: '1',
closeString: '23',
),

_Example(
'Add paired symbols before same close char',
initialValue: TextEditingValue(
text: 'string)',
// \ cursor
selection: TextSelection.collapsed(offset: 6),
),
expected: TextEditingValue(
text: 'string())',
// \ cursor
selection: TextSelection.collapsed(offset: 7),
),
openChar: '(',
closeString: ')',
),

_Example(
'Empty initial string',
initialValue: TextEditingValue(
// ignore: avoid_redundant_argument_values
text: '',
// \ cursor
selection: TextSelection.collapsed(offset: 0),
),
expected: TextEditingValue(
text: '()',
// \ cursor
selection: TextSelection.collapsed(offset: 1),
),
openChar: '(',
closeString: ')',
),
];

for (final example in examples) {
final modifier = PairedSymbolsCodeModifier(
openChar: example.openChar,
closeString: example.closeString,
);

final controller = CodeController(
modifiers: [modifier],
);

controller.value = example.initialValue;

controller.value = _addCharToSelectedPosition(
controller.value,
example.openChar,
);

expect(
controller.value,
example.expected,
reason: example.name,
);
}
});
}

TextEditingValue _addCharToSelectedPosition(
TextEditingValue value,
String char,
) {
final selection = value.selection;
final text = value.text;

final newText = text.substring(0, selection.start) +
char +
text.substring(selection.start);

return TextEditingValue(
text: newText,
selection: TextSelection.collapsed(
offset: selection.start + char.length,
),
);
}

class _Example {
final String name;
final TextEditingValue initialValue;
final TextEditingValue expected;
final String openChar;
final String closeString;

const _Example(
this.name, {
required this.initialValue,
required this.expected,
required this.openChar,
required this.closeString,
});
}