chore: add linters conforming to style guide (#59)

Relates: #56 #57
This commit is contained in:
Mathias Mogensen 2023-04-12 14:37:50 +02:00 • committed by GitHub
commit cf5e83d370
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 1875 additions and 1198 deletions

View file

@ -2,3 +2,13 @@ include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at # Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options # https://dart.dev/guides/language/analysis-options
linter:
rules:
- always_declare_return_types
- require_trailing_commas
- unnecessary_raw_strings
- use_decorated_box
analyzer:
exclude:
- lib/src/l10n/**

View file

@ -52,7 +52,8 @@ extension TextCommands on EditorState {
if (BuiltInAttributeKey.globalStyleKeys.contains(key)) { if (BuiltInAttributeKey.globalStyleKeys.contains(key)) {
final attr = n.attributes final attr = n.attributes
..removeWhere( ..removeWhere(
(key, _) => BuiltInAttributeKey.globalStyleKeys.contains(key)) (key, _) => BuiltInAttributeKey.globalStyleKeys.contains(key),
)
..addAll(attributes) ..addAll(attributes)
..addAll({ ..addAll({
BuiltInAttributeKey.subtype: key, BuiltInAttributeKey.subtype: key,

View file

@ -247,7 +247,9 @@ class Delta extends Iterable<TextOperation> {
@override @override
int get length { int get length {
return _operations.fold( return _operations.fold(
0, (previousValue, element) => previousValue + element.length); 0,
(previousValue, element) => previousValue + element.length,
);
} }
/// Returns a Delta that is equivalent to applying the operations of own Delta, followed by another Delta. /// Returns a Delta that is equivalent to applying the operations of own Delta, followed by another Delta.

View file

@ -58,11 +58,13 @@ class Transaction {
/// The [attributes] will be merged into the existing attributes. /// The [attributes] will be merged into the existing attributes.
void updateNode(Node node, Attributes attributes) { void updateNode(Node node, Attributes attributes) {
final inverted = invertAttributes(node.attributes, attributes); final inverted = invertAttributes(node.attributes, attributes);
add(UpdateOperation( add(
UpdateOperation(
node.path, node.path,
{...attributes}, {...attributes},
inverted, inverted,
)); ),
);
} }
/// Deletes the [Node] in the document. /// Deletes the [Node] in the document.
@ -170,10 +172,12 @@ extension TextTransaction on Transaction {
..delete(firstLength - firstOffset) ..delete(firstLength - firstOffset)
..addAll(second.delta.slice(secondOffset, secondLength)), ..addAll(second.delta.slice(secondOffset, secondLength)),
); );
afterSelection = Selection.collapsed(Position( afterSelection = Selection.collapsed(
Position(
path: first.path, path: first.path,
offset: firstOffset, offset: firstOffset,
)); ),
);
} }
void splitText(TextNode textNode, int offset) { void splitText(TextNode textNode, int offset) {
@ -188,10 +192,12 @@ extension TextTransaction on Transaction {
delta: second, delta: second,
), ),
); );
afterSelection = Selection.collapsed(Position( afterSelection = Selection.collapsed(
Position(
path: path, path: path,
offset: 0, offset: 0,
)); ),
);
} }
/// Inserts the text content at a specified index. /// Inserts the text content at a specified index.

View file

@ -80,7 +80,8 @@ extension DeltaAttributesExtensions on Attributes {
if (containsKey(BuiltInAttributeKey.backgroundColor) && if (containsKey(BuiltInAttributeKey.backgroundColor) &&
this[BuiltInAttributeKey.backgroundColor] is String) { this[BuiltInAttributeKey.backgroundColor] is String) {
return Color( return Color(
int.tryParse(this[BuiltInAttributeKey.backgroundColor]) ?? whiteInt); int.tryParse(this[BuiltInAttributeKey.backgroundColor]) ?? whiteInt,
);
} }
return null; return null;
} }

View file

@ -300,16 +300,20 @@ class Overlay extends StatefulWidget {
final List<DiagnosticsNode> information = <DiagnosticsNode>[ final List<DiagnosticsNode> information = <DiagnosticsNode>[
ErrorSummary('No Overlay widget found.'), ErrorSummary('No Overlay widget found.'),
ErrorDescription( ErrorDescription(
'${debugRequiredFor.runtimeType} widgets require an Overlay widget ancestor for correct operation.'), '${debugRequiredFor.runtimeType} widgets require an Overlay widget ancestor for correct operation.',
),
ErrorHint( ErrorHint(
'The most common way to add an Overlay to an application is to include a MaterialApp or Navigator widget in the runApp() call.'), 'The most common way to add an Overlay to an application is to include a MaterialApp or Navigator widget in the runApp() call.',
),
DiagnosticsProperty<Widget>( DiagnosticsProperty<Widget>(
'The specific widget that failed to find an overlay was', 'The specific widget that failed to find an overlay was',
debugRequiredFor, debugRequiredFor,
style: DiagnosticsTreeStyle.errorProperty), style: DiagnosticsTreeStyle.errorProperty,
),
if (context.widget != debugRequiredFor) if (context.widget != debugRequiredFor)
context.describeElement( context.describeElement(
'The context from which that widget was searching for an overlay was'), 'The context from which that widget was searching for an overlay was',
),
]; ];
throw FlutterError.fromParts(information); throw FlutterError.fromParts(information);
@ -352,10 +356,14 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
/// It is an error to specify both `above` and `below`. /// It is an error to specify both `above` and `below`.
void insert(OverlayEntry entry, {OverlayEntry? below, OverlayEntry? above}) { void insert(OverlayEntry entry, {OverlayEntry? below, OverlayEntry? above}) {
assert(_debugVerifyInsertPosition(above, below)); assert(_debugVerifyInsertPosition(above, below));
assert(!_entries.contains(entry), assert(
'The specified entry is already present in the Overlay.'); !_entries.contains(entry),
assert(entry._overlay == null, 'The specified entry is already present in the Overlay.',
'The specified entry is already present in another Overlay.'); );
assert(
entry._overlay == null,
'The specified entry is already present in another Overlay.',
);
entry._overlay = this; entry._overlay = this;
setState(() { setState(() {
_entries.insert(_insertionIndex(below, above), entry); _entries.insert(_insertionIndex(below, above), entry);
@ -369,8 +377,11 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
/// Otherwise, the entries are inserted on top. /// Otherwise, the entries are inserted on top.
/// ///
/// It is an error to specify both `above` and `below`. /// It is an error to specify both `above` and `below`.
void insertAll(Iterable<OverlayEntry> entries, void insertAll(
{OverlayEntry? below, OverlayEntry? above}) { Iterable<OverlayEntry> entries, {
OverlayEntry? below,
OverlayEntry? above,
}) {
assert(_debugVerifyInsertPosition(above, below)); assert(_debugVerifyInsertPosition(above, below));
assert( assert(
entries.every((OverlayEntry entry) => !_entries.contains(entry)), entries.every((OverlayEntry entry) => !_entries.contains(entry)),
@ -390,8 +401,11 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
}); });
} }
bool _debugVerifyInsertPosition(OverlayEntry? above, OverlayEntry? below, bool _debugVerifyInsertPosition(
{Iterable<OverlayEntry>? newEntries}) { OverlayEntry? above,
OverlayEntry? below, {
Iterable<OverlayEntry>? newEntries,
}) {
assert( assert(
above == null || below == null, above == null || below == null,
'Only one of `above` and `below` may be specified.', 'Only one of `above` and `below` may be specified.',
@ -430,21 +444,29 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
/// below. /// below.
/// ///
/// It is an error to specify both `above` and `below`. /// It is an error to specify both `above` and `below`.
void rearrange(Iterable<OverlayEntry> newEntries, void rearrange(
{OverlayEntry? below, OverlayEntry? above}) { Iterable<OverlayEntry> newEntries, {
OverlayEntry? below,
OverlayEntry? above,
}) {
final List<OverlayEntry> newEntriesList = newEntries is List<OverlayEntry> final List<OverlayEntry> newEntriesList = newEntries is List<OverlayEntry>
? newEntries ? newEntries
: newEntries.toList(growable: false); : newEntries.toList(growable: false);
assert( assert(
_debugVerifyInsertPosition(above, below, newEntries: newEntriesList)); _debugVerifyInsertPosition(above, below, newEntries: newEntriesList),
);
assert( assert(
newEntriesList.every((OverlayEntry entry) => newEntriesList.every(
entry._overlay == null || entry._overlay == this), (OverlayEntry entry) =>
entry._overlay == null || entry._overlay == this,
),
'One or more of the specified entries are already present in another Overlay.', 'One or more of the specified entries are already present in another Overlay.',
); );
assert( assert(
newEntriesList.every((OverlayEntry entry) => newEntriesList.every(
_entries.indexOf(entry) == _entries.lastIndexOf(entry)), (OverlayEntry entry) =>
_entries.indexOf(entry) == _entries.lastIndexOf(entry),
),
'One or more of the specified entries are specified multiple times.', 'One or more of the specified entries are specified multiple times.',
); );
if (newEntriesList.isEmpty) return; if (newEntriesList.isEmpty) return;
@ -509,17 +531,21 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
final OverlayEntry entry = _entries[i]; final OverlayEntry entry = _entries[i];
if (onstage) { if (onstage) {
onstageCount += 1; onstageCount += 1;
children.add(_OverlayEntryWidget( children.add(
_OverlayEntryWidget(
key: entry._key, key: entry._key,
entry: entry, entry: entry,
)); ),
);
if (entry.opaque) onstage = false; if (entry.opaque) onstage = false;
} else if (entry.maintainState) { } else if (entry.maintainState) {
children.add(_OverlayEntryWidget( children.add(
_OverlayEntryWidget(
key: entry._key, key: entry._key,
entry: entry, entry: entry,
tickerEnabled: false, tickerEnabled: false,
)); ),
);
} }
} }
return _Theatre( return _Theatre(
@ -684,26 +710,34 @@ class _RenderTheatre extends RenderBox
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
return RenderStack.getIntrinsicDimension(_firstOnstageChild, return RenderStack.getIntrinsicDimension(
(RenderBox child) => child.getMinIntrinsicWidth(height)); _firstOnstageChild,
(RenderBox child) => child.getMinIntrinsicWidth(height),
);
} }
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
return RenderStack.getIntrinsicDimension(_firstOnstageChild, return RenderStack.getIntrinsicDimension(
(RenderBox child) => child.getMaxIntrinsicWidth(height)); _firstOnstageChild,
(RenderBox child) => child.getMaxIntrinsicWidth(height),
);
} }
@override @override
double computeMinIntrinsicHeight(double width) { double computeMinIntrinsicHeight(double width) {
return RenderStack.getIntrinsicDimension(_firstOnstageChild, return RenderStack.getIntrinsicDimension(
(RenderBox child) => child.getMinIntrinsicHeight(width)); _firstOnstageChild,
(RenderBox child) => child.getMinIntrinsicHeight(width),
);
} }
@override @override
double computeMaxIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) {
return RenderStack.getIntrinsicDimension(_firstOnstageChild, return RenderStack.getIntrinsicDimension(
(RenderBox child) => child.getMaxIntrinsicHeight(width)); _firstOnstageChild,
(RenderBox child) => child.getMaxIntrinsicHeight(width),
);
} }
@override @override
@ -764,7 +798,11 @@ class _RenderTheatre extends RenderBox
_resolvedAlignment!.alongOffset(size - child.size as Offset); _resolvedAlignment!.alongOffset(size - child.size as Offset);
} else { } else {
_hasVisualOverflow = RenderStack.layoutPositionedChild( _hasVisualOverflow = RenderStack.layoutPositionedChild(
child, childParentData, size, _resolvedAlignment!) || child,
childParentData,
size,
_resolvedAlignment!,
) ||
_hasVisualOverflow; _hasVisualOverflow;
} }

View file

@ -22,17 +22,17 @@ class HistoryItem extends LinkedListEntry<HistoryItem> {
/// to the item. /// to the item.
/// ///
/// The caller should create a new [HistoryItem]. /// The caller should create a new [HistoryItem].
seal() { void seal() {
_sealed = true; _sealed = true;
} }
bool get sealed => _sealed; bool get sealed => _sealed;
add(Operation op) { void add(Operation op) {
operations.add(op); operations.add(op);
} }
addAll(Iterable<Operation> iterable) { void addAll(Iterable<Operation> iterable) {
operations.addAll(iterable); operations.addAll(iterable);
} }
@ -56,7 +56,7 @@ class FixedSizeStack {
FixedSizeStack(this.maxSize); FixedSizeStack(this.maxSize);
push(HistoryItem stackItem) { void push(HistoryItem stackItem) {
if (_list.length >= maxSize) { if (_list.length >= maxSize) {
_list.remove(_list.first); _list.remove(_list.first);
} }
@ -74,7 +74,7 @@ class FixedSizeStack {
return last; return last;
} }
clear() { void clear() {
_list.clear(); _list.clear();
} }
@ -110,7 +110,7 @@ class UndoManager {
return last; return last;
} }
undo() { void undo() {
Log.editor.debug('undo'); Log.editor.debug('undo');
final s = state; final s = state;
if (s == null) { if (s == null) {
@ -130,7 +130,7 @@ class UndoManager {
); );
} }
redo() { void redo() {
Log.editor.debug('redo'); Log.editor.debug('redo');
final s = state; final s = state;
if (s == null) { if (s == null) {

View file

@ -103,7 +103,8 @@ class HTMLToNodesConverter {
for (final child in element.nodes.toList()) { for (final child in element.nodes.toList()) {
if (child is html.Element) { if (child is html.Element) {
result.addAll( result.addAll(
_handleElement(child, {"subtype": BuiltInAttributeKey.quote})); _handleElement(child, {"subtype": BuiltInAttributeKey.quote}),
);
} }
} }
@ -115,8 +116,10 @@ class HTMLToNodesConverter {
return _handleContainer(childNodes); return _handleContainer(childNodes);
} }
List<Node> _handleElement(html.Element element, List<Node> _handleElement(
[Map<String, dynamic>? attributes]) { html.Element element, [
Map<String, dynamic>? attributes,
]) {
if (element.localName == HTMLTag.h1) { if (element.localName == HTMLTag.h1) {
return [_handleHeadingElement(element, HTMLTag.h1)]; return [_handleHeadingElement(element, HTMLTag.h1)];
} else if (element.localName == HTMLTag.h2) { } else if (element.localName == HTMLTag.h2) {
@ -142,8 +145,10 @@ class HTMLToNodesConverter {
} }
} }
Node _handleParagraph(html.Element element, Node _handleParagraph(
[Map<String, dynamic>? attributes]) { html.Element element, [
Map<String, dynamic>? attributes,
]) {
_inParagraph = true; _inParagraph = true;
final node = _handleRichText(element, attributes); final node = _handleRichText(element, attributes);
_inParagraph = false; _inParagraph = false;
@ -171,7 +176,8 @@ class HTMLToNodesConverter {
} }
Attributes? _getDeltaAttributesFromHtmlAttributes( Attributes? _getDeltaAttributesFromHtmlAttributes(
LinkedHashMap<Object, String> htmlAttributes) { LinkedHashMap<Object, String> htmlAttributes,
) {
final attrs = <String, dynamic>{}; final attrs = <String, dynamic>{};
final styleString = htmlAttributes["style"]; final styleString = htmlAttributes["style"];
final cssMap = _cssStringToMap(styleString); final cssMap = _cssStringToMap(styleString);
@ -209,7 +215,7 @@ class HTMLToNodesConverter {
return attrs.isEmpty ? null : attrs; return attrs.isEmpty ? null : attrs;
} }
_assignTextDecorations(Attributes attrs, String decorationStr) { void _assignTextDecorations(Attributes attrs, String decorationStr) {
final decorations = decorationStr.split(" "); final decorations = decorationStr.split(" ");
for (final d in decorations) { for (final d in decorations) {
if (d == "line-through") { if (d == "line-through") {
@ -220,7 +226,7 @@ class HTMLToNodesConverter {
} }
} }
_handleRichTextElement(Delta delta, html.Element element) { void _handleRichTextElement(Delta delta, html.Element element) {
if (element.localName == HTMLTag.span) { if (element.localName == HTMLTag.span) {
delta.insert( delta.insert(
element.text, element.text,
@ -242,11 +248,18 @@ class HTMLToNodesConverter {
attributes: {BuiltInAttributeKey.italic: true}, attributes: {BuiltInAttributeKey.italic: true},
); );
} else if (element.localName == HTMLTag.underline) { } else if (element.localName == HTMLTag.underline) {
delta.insert(element.text, delta.insert(
attributes: {BuiltInAttributeKey.underline: true}); element.text,
attributes: {BuiltInAttributeKey.underline: true},
);
} else if ([HTMLTag.italic, HTMLTag.em].contains(element.localName)) {
delta
.insert(element.text, attributes: {BuiltInAttributeKey.italic: true});
} else if (element.localName == HTMLTag.del) { } else if (element.localName == HTMLTag.del) {
delta.insert(element.text, delta.insert(
attributes: {BuiltInAttributeKey.strikethrough: true}); element.text,
attributes: {BuiltInAttributeKey.strikethrough: true},
);
} else if (element.localName == HTMLTag.code) { } else if (element.localName == HTMLTag.code) {
delta.insert(element.text, attributes: {BuiltInAttributeKey.code: true}); delta.insert(element.text, attributes: {BuiltInAttributeKey.code: true});
} else { } else {
@ -258,8 +271,10 @@ class HTMLToNodesConverter {
/// be regarded as a checkbox block. /// be regarded as a checkbox block.
/// ///
/// A container contains a <img /> will be regarded as a image block /// A container contains a <img /> will be regarded as a image block
Node _handleRichText(html.Element element, Node _handleRichText(
[Map<String, dynamic>? attributes]) { html.Element element, [
Map<String, dynamic>? attributes,
]) {
final image = element.querySelector(HTMLTag.image); final image = element.querySelector(HTMLTag.image);
if (image != null) { if (image != null) {
final imageNode = _handleImage(image); final imageNode = _handleImage(image);
@ -284,13 +299,16 @@ class HTMLToNodesConverter {
} }
} }
final textNode = TextNode(delta: delta, attributes: { final textNode = TextNode(
delta: delta,
attributes: {
if (attributes != null) ...attributes, if (attributes != null) ...attributes,
if (isCheckbox) ...{ if (isCheckbox) ...{
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
BuiltInAttributeKey.checkbox: checked, BuiltInAttributeKey.checkbox: checked,
} }
}); },
);
return textNode; return textNode;
} }
@ -306,10 +324,12 @@ class HTMLToNodesConverter {
List<Node> _handleUnorderedList(html.Element element) { List<Node> _handleUnorderedList(html.Element element) {
final result = <Node>[]; final result = <Node>[];
for (var child in element.children) { for (var child in element.children) {
result.addAll(_handleListElement( result.addAll(
_handleListElement(
child, child,
{"subtype": BuiltInAttributeKey.bulletedList}, {"subtype": BuiltInAttributeKey.bulletedList},
)); ),
);
} }
return result; return result;
} }
@ -318,8 +338,12 @@ class HTMLToNodesConverter {
final result = <Node>[]; final result = <Node>[];
for (var i = 0; i < element.children.length; i++) { for (var i = 0; i < element.children.length; i++) {
final child = element.children[i]; final child = element.children[i];
result.addAll(_handleListElement( result.addAll(
child, {"subtype": BuiltInAttributeKey.numberList, "number": i + 1})); _handleListElement(
child,
{"subtype": BuiltInAttributeKey.numberList, "number": i + 1},
),
);
} }
return result; return result;
} }
@ -332,11 +356,14 @@ class HTMLToNodesConverter {
delta.insert(element.text); delta.insert(element.text);
return TextNode( return TextNode(
attributes: {"subtype": "heading", "heading": headingStyle}, attributes: {"subtype": "heading", "heading": headingStyle},
delta: delta); delta: delta,
);
} }
List<Node> _handleListElement(html.Element element, List<Node> _handleListElement(
[Map<String, dynamic>? attributes]) { html.Element element, [
Map<String, dynamic>? attributes,
]) {
final result = <Node>[]; final result = <Node>[];
final childNodes = element.nodes.toList(); final childNodes = element.nodes.toList();
for (final child in childNodes) { for (final child in childNodes) {
@ -367,15 +394,19 @@ class NodesToHTMLConverter {
/// This container is used to save the list elements temporarily. /// This container is used to save the list elements temporarily.
html.Element? _stashListContainer; html.Element? _stashListContainer;
NodesToHTMLConverter( NodesToHTMLConverter({
{required this.nodes, this.startOffset, this.endOffset}) { required this.nodes,
this.startOffset,
this.endOffset,
}) {
if (nodes.isEmpty) { if (nodes.isEmpty) {
return; return;
} else if (nodes.length == 1) { } else if (nodes.length == 1) {
final first = nodes.first; final first = nodes.first;
if (first is TextNode) { if (first is TextNode) {
nodes[0] = first.copyWith( nodes[0] = first.copyWith(
delta: first.delta.slice(startOffset ?? 0, endOffset)); delta: first.delta.slice(startOffset ?? 0, endOffset),
);
} }
} else { } else {
final first = nodes.first; final first = nodes.first;
@ -411,16 +442,17 @@ class NodesToHTMLConverter {
return _result; return _result;
} }
_addTextNode(TextNode textNode, {int? end}) { void _addTextNode(TextNode textNode, {int? end}) {
_addElement(textNode, _textNodeToHtml(textNode, end: end)); _addElement(textNode, _textNodeToHtml(textNode, end: end));
} }
_addElement(TextNode textNode, html.Element element) { void _addElement(TextNode textNode, html.Element element) {
if (element.localName == HTMLTag.list) { if (element.localName == HTMLTag.list) {
final isNumbered = final isNumbered =
textNode.attributes["subtype"] == BuiltInAttributeKey.numberList; textNode.attributes["subtype"] == BuiltInAttributeKey.numberList;
_stashListContainer ??= html.Element.tag( _stashListContainer ??= html.Element.tag(
isNumbered ? HTMLTag.orderedList : HTMLTag.unorderedList); isNumbered ? HTMLTag.orderedList : HTMLTag.unorderedList,
);
_stashListContainer?.append(element); _stashListContainer?.append(element);
} else { } else {
if (_stashListContainer != null) { if (_stashListContainer != null) {
@ -434,18 +466,22 @@ class NodesToHTMLConverter {
String toHTMLString() { String toHTMLString() {
final elements = toHTMLNodes(); final elements = toHTMLNodes();
final copyString = elements.fold<String>( final copyString = elements.fold<String>(
"", ((previousValue, element) => previousValue + stringify(element))); "",
((previousValue, element) => previousValue + stringify(element)),
);
return copyString; return copyString;
} }
html.Element _textNodeToHtml(TextNode textNode, {int? end}) { html.Element _textNodeToHtml(TextNode textNode, {int? end}) {
String? subType = textNode.attributes["subtype"]; String? subType = textNode.attributes["subtype"];
String? heading = textNode.attributes["heading"]; String? heading = textNode.attributes["heading"];
return _deltaToHtml(textNode.delta, return _deltaToHtml(
textNode.delta,
subType: subType, subType: subType,
heading: heading, heading: heading,
end: end, end: end,
checked: textNode.attributes["checkbox"] == true); checked: textNode.attributes["checkbox"] == true,
);
} }
String _textDecorationsFromAttributes(Attributes attributes) { String _textDecorationsFromAttributes(Attributes attributes) {
@ -515,8 +551,13 @@ class NodesToHTMLConverter {
/// ```html /// ```html
/// <span style="...">Text</span> /// <span style="...">Text</span>
/// ``` /// ```
html.Element _deltaToHtml(Delta delta, html.Element _deltaToHtml(
{String? subType, String? heading, int? end, bool? checked}) { Delta delta, {
String? subType,
String? heading,
int? end,
bool? checked,
}) {
if (end != null) { if (end != null) {
delta = delta.slice(0, end); delta = delta.slice(0, end);
} }

View file

@ -80,7 +80,7 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote,
}, },
); );
} else if (text.isNotEmpty && RegExp(r'^-*').stringMatch(text) == text) { } else if (text.isNotEmpty && RegExp('^-*').stringMatch(text) == text) {
return Node(type: 'divider'); return Node(type: 'divider');
} }

View file

@ -22,13 +22,17 @@ Document markdownToDocument(
/// Converts a [Document] to markdown. /// Converts a [Document] to markdown.
/// ///
/// [customParsers] is a list of custom parsers that will be used to parse the markdown. /// [customParsers] is a list of custom parsers that will be used to parse the markdown.
String documentToMarkdown(Document document, String documentToMarkdown(
{List<NodeParser> customParsers = const []}) { Document document, {
return AppFlowyEditorMarkdownCodec(encodeParsers: [ List<NodeParser> customParsers = const [],
}) {
return AppFlowyEditorMarkdownCodec(
encodeParsers: [
...customParsers, ...customParsers,
const TextNodeParser(), const TextNodeParser(),
const ImageNodeParser(), const ImageNodeParser(),
]).encode(document); ],
).encode(document);
} }
class AppFlowyEditorMarkdownCodec extends Codec<Document, String> { class AppFlowyEditorMarkdownCodec extends Codec<Document, String> {

View file

@ -106,7 +106,10 @@ class _ColorPickerState extends State<ColorPicker> {
} }
Widget _buildColorItems( Widget _buildColorItems(
_ColorType type, List<ColorOption> options, String? selectedColor) { _ColorType type,
List<ColorOption> options,
String? selectedColor,
) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,

View file

@ -52,7 +52,7 @@ class _LinkMenuState extends State<LinkMenu> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(
width: 350, width: 350,
child: Container( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: style?.selectionMenuBackgroundColor ?? Colors.white, color: style?.selectionMenuBackgroundColor ?? Colors.white,
boxShadow: [ boxShadow: [

View file

@ -79,7 +79,9 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
@override @override
Position end() => Position( Position end() => Position(
path: widget.textNode.path, offset: widget.textNode.toPlainText().length); path: widget.textNode.path,
offset: widget.textNode.toPlainText().length,
);
@override @override
Rect? getCursorRectInPosition(Position position) { Rect? getCursorRectInPosition(Position position) {
@ -92,7 +94,9 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
cursorHeight = cursorHeight =
_placeholderRenderParagraph?.getFullHeightForCaret(textPosition); _placeholderRenderParagraph?.getFullHeightForCaret(textPosition);
cursorOffset = _placeholderRenderParagraph?.getOffsetForCaret( cursorOffset = _placeholderRenderParagraph?.getOffsetForCaret(
textPosition, Rect.zero) ?? textPosition,
Rect.zero,
) ??
Offset.zero; Offset.zero;
} }
if (widget.cursorHeight != null && cursorHeight != null) { if (widget.cursorHeight != null && cursorHeight != null) {
@ -139,8 +143,9 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
@override @override
List<Rect> getRectsInSelection(Selection selection) { List<Rect> getRectsInSelection(Selection selection) {
assert(selection.isSingle && assert(
selection.start.path.equals(widget.textNode.path)); selection.isSingle && selection.start.path.equals(widget.textNode.path),
);
final textSelection = TextSelection( final textSelection = TextSelection(
baseOffset: selection.start.offset, baseOffset: selection.start.offset,
@ -196,7 +201,9 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
return RichText( return RichText(
key: _placeholderTextKey, key: _placeholderTextKey,
textHeightBehavior: const TextHeightBehavior( textHeightBehavior: const TextHeightBehavior(
applyHeightToFirstAscent: false, applyHeightToLastDescent: false), applyHeightToFirstAscent: false,
applyHeightToLastDescent: false,
),
text: widget.placeholderTextSpanDecorator != null text: widget.placeholderTextSpanDecorator != null
? widget.placeholderTextSpanDecorator!(textSpan) ? widget.placeholderTextSpanDecorator!(textSpan)
: textSpan, : textSpan,
@ -303,7 +310,9 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
} }
GestureRecognizer _buildTapHrefGestureRecognizer( GestureRecognizer _buildTapHrefGestureRecognizer(
String href, Selection selection) { String href,
Selection selection,
) {
Timer? timer; Timer? timer;
var tapCount = 0; var tapCount = 0;
final tapGestureRecognizer = TapGestureRecognizer() final tapGestureRecognizer = TapGestureRecognizer()

View file

@ -53,7 +53,7 @@ class CursorWidgetState extends State<CursorWidget> {
} }
/// force the cursor widget to show for a while /// force the cursor widget to show for a while
show() { void show() {
setState(() { setState(() {
showCursor = true; showCursor = true;
}); });

View file

@ -40,10 +40,12 @@ class _SelectionMenuItemWidgetState extends State<SelectionMenuItemWidget> {
style: ButtonStyle( style: ButtonStyle(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
overlayColor: MaterialStateProperty.all( overlayColor: MaterialStateProperty.all(
editorStyle.selectionMenuItemSelectedColor), editorStyle.selectionMenuItemSelectedColor,
),
backgroundColor: widget.isSelected backgroundColor: widget.isSelected
? MaterialStateProperty.all( ? MaterialStateProperty.all(
editorStyle.selectionMenuItemSelectedColor) editorStyle.selectionMenuItemSelectedColor,
)
: MaterialStateProperty.all(Colors.transparent), : MaterialStateProperty.all(Colors.transparent),
), ),
label: Text( label: Text(

View file

@ -83,10 +83,13 @@ class SelectionMenu implements SelectionMenuService {
_alignment = Alignment.topLeft; _alignment = Alignment.topLeft;
} }
_topLeft = offset; _topLeft = offset;
_offset = Offset(offset.dx, _offset = Offset(
showBelow ? offset.dy : MediaQuery.of(context).size.height - offset.dy); offset.dx,
showBelow ? offset.dy : MediaQuery.of(context).size.height - offset.dy,
);
_selectionMenuEntry = OverlayEntry(builder: (context) { _selectionMenuEntry = OverlayEntry(
builder: (context) {
return Positioned( return Positioned(
top: showBelow ? _offset.dy : null, top: showBelow ? _offset.dy : null,
bottom: showBelow ? null : _offset.dy, bottom: showBelow ? null : _offset.dy,
@ -111,7 +114,8 @@ class SelectionMenu implements SelectionMenuService {
), ),
), ),
); );
}); },
);
Overlay.of(context).insert(_selectionMenuEntry!); Overlay.of(context).insert(_selectionMenuEntry!);
@ -241,7 +245,10 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
]; ];
Widget _selectionMenuIcon( Widget _selectionMenuIcon(
String name, EditorState editorState, bool onSelected) { String name,
EditorState editorState,
bool onSelected,
) {
return FlowySvg( return FlowySvg(
name: 'selection_menu/$name', name: 'selection_menu/$name',
color: onSelected color: onSelected

View file

@ -119,7 +119,11 @@ class SelectionMenuItem {
transaction transaction
..insertNode(path, node) ..insertNode(path, node)
..afterSelection = updateSelection?.call( ..afterSelection = updateSelection?.call(
editorState, path, bReplace, bInsertBefore) ?? editorState,
path,
bReplace,
bInsertBefore,
) ??
selection; selection;
if (bReplace) { if (bReplace) {
@ -216,7 +220,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
return Focus( return Focus(
focusNode: _focusNode, focusNode: _focusNode,
onKey: _onKey, onKey: _onKey,
child: Container( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: widget.editorState.editorStyle.selectionMenuBackgroundColor, color: widget.editorState.editorStyle.selectionMenuBackgroundColor,
boxShadow: [ boxShadow: [
@ -248,24 +252,30 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
List<Widget> itemWidgets = []; List<Widget> itemWidgets = [];
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
if (i != 0 && i % (widget.maxItemInRow) == 0) { if (i != 0 && i % (widget.maxItemInRow) == 0) {
columns.add(Column( columns.add(
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: itemWidgets, children: itemWidgets,
)); ),
);
itemWidgets = []; itemWidgets = [];
} }
itemWidgets.add(SelectionMenuItemWidget( itemWidgets.add(
SelectionMenuItemWidget(
item: items[i], item: items[i],
isSelected: selectedIndex == i, isSelected: selectedIndex == i,
editorState: widget.editorState, editorState: widget.editorState,
menuService: widget.menuService, menuService: widget.menuService,
)); ),
);
} }
if (itemWidgets.isNotEmpty) { if (itemWidgets.isNotEmpty) {
columns.add(Column( columns.add(
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: itemWidgets, children: itemWidgets,
)); ),
);
itemWidgets = []; itemWidgets = [];
} }
return Row( return Row(

View file

@ -118,7 +118,9 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
@override @override
ThemeExtension<EditorStyle> lerp( ThemeExtension<EditorStyle> lerp(
ThemeExtension<EditorStyle>? other, double t) { ThemeExtension<EditorStyle>? other,
double t,
) {
if (other == null || other is! EditorStyle) { if (other == null || other is! EditorStyle) {
return this; return this;
} }
@ -129,21 +131,35 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
textPadding: EdgeInsets.lerp(textPadding, other.textPadding, t), textPadding: EdgeInsets.lerp(textPadding, other.textPadding, t),
selectionColor: Color.lerp(selectionColor, other.selectionColor, t), selectionColor: Color.lerp(selectionColor, other.selectionColor, t),
selectionMenuBackgroundColor: Color.lerp( selectionMenuBackgroundColor: Color.lerp(
selectionMenuBackgroundColor, other.selectionMenuBackgroundColor, t), selectionMenuBackgroundColor,
other.selectionMenuBackgroundColor,
t,
),
selectionMenuItemTextColor: Color.lerp( selectionMenuItemTextColor: Color.lerp(
selectionMenuItemTextColor, other.selectionMenuItemTextColor, t), selectionMenuItemTextColor,
other.selectionMenuItemTextColor,
t,
),
selectionMenuItemIconColor: Color.lerp( selectionMenuItemIconColor: Color.lerp(
selectionMenuItemIconColor, other.selectionMenuItemIconColor, t), selectionMenuItemIconColor,
other.selectionMenuItemIconColor,
t,
),
selectionMenuItemSelectedTextColor: Color.lerp( selectionMenuItemSelectedTextColor: Color.lerp(
selectionMenuItemSelectedTextColor, selectionMenuItemSelectedTextColor,
other.selectionMenuItemSelectedTextColor, other.selectionMenuItemSelectedTextColor,
t), t,
),
selectionMenuItemSelectedIconColor: Color.lerp( selectionMenuItemSelectedIconColor: Color.lerp(
selectionMenuItemSelectedIconColor, selectionMenuItemSelectedIconColor,
other.selectionMenuItemSelectedIconColor, other.selectionMenuItemSelectedIconColor,
t), t,
selectionMenuItemSelectedColor: Color.lerp(selectionMenuItemSelectedColor, ),
other.selectionMenuItemSelectedColor, t), selectionMenuItemSelectedColor: Color.lerp(
selectionMenuItemSelectedColor,
other.selectionMenuItemSelectedColor,
t,
),
textStyle: TextStyle.lerp(textStyle, other.textStyle, t), textStyle: TextStyle.lerp(textStyle, other.textStyle, t),
placeholderTextStyle: placeholderTextStyle:
TextStyle.lerp(placeholderTextStyle, other.placeholderTextStyle, t), TextStyle.lerp(placeholderTextStyle, other.placeholderTextStyle, t),

View file

@ -19,11 +19,17 @@ Iterable<ThemeExtension<dynamic>> get darkPluginStyleExtension => [
]; ];
typedef TextStyleCustomizer = TextStyle Function( typedef TextStyleCustomizer = TextStyle Function(
EditorState editorState, TextNode textNode); EditorState editorState,
TextNode textNode,
);
typedef PaddingCustomizer = EdgeInsets Function( typedef PaddingCustomizer = EdgeInsets Function(
EditorState editorState, TextNode textNode); EditorState editorState,
TextNode textNode,
);
typedef IconCustomizer = Widget Function( typedef IconCustomizer = Widget Function(
EditorState editorState, TextNode textNode); EditorState editorState,
TextNode textNode,
);
class HeadingPluginStyle extends ThemeExtension<HeadingPluginStyle> { class HeadingPluginStyle extends ThemeExtension<HeadingPluginStyle> {
const HeadingPluginStyle({ const HeadingPluginStyle({
@ -47,7 +53,9 @@ class HeadingPluginStyle extends ThemeExtension<HeadingPluginStyle> {
@override @override
ThemeExtension<HeadingPluginStyle> lerp( ThemeExtension<HeadingPluginStyle> lerp(
ThemeExtension<HeadingPluginStyle>? other, double t) { ThemeExtension<HeadingPluginStyle>? other,
double t,
) {
if (other is! HeadingPluginStyle) { if (other is! HeadingPluginStyle) {
return this; return this;
} }
@ -105,7 +113,9 @@ class CheckboxPluginStyle extends ThemeExtension<CheckboxPluginStyle> {
@override @override
ThemeExtension<CheckboxPluginStyle> lerp( ThemeExtension<CheckboxPluginStyle> lerp(
ThemeExtension<CheckboxPluginStyle>? other, double t) { ThemeExtension<CheckboxPluginStyle>? other,
double t,
) {
if (other is! CheckboxPluginStyle) { if (other is! CheckboxPluginStyle) {
return this; return this;
} }
@ -167,7 +177,9 @@ class BulletedListPluginStyle extends ThemeExtension<BulletedListPluginStyle> {
@override @override
ThemeExtension<BulletedListPluginStyle> lerp( ThemeExtension<BulletedListPluginStyle> lerp(
ThemeExtension<BulletedListPluginStyle>? other, double t) { ThemeExtension<BulletedListPluginStyle>? other,
double t,
) {
if (other is! BulletedListPluginStyle) { if (other is! BulletedListPluginStyle) {
return this; return this;
} }
@ -194,7 +206,8 @@ class BulletedListPluginStyle extends ThemeExtension<BulletedListPluginStyle> {
}, },
); );
static final dark = light.copyWith(icon: (_, __) { static final dark = light.copyWith(
icon: (_, __) {
const iconSize = Size.square(20.0); const iconSize = Size.square(20.0);
const iconPadding = EdgeInsets.only(right: 5.0); const iconPadding = EdgeInsets.only(right: 5.0);
return FlowySvg( return FlowySvg(
@ -204,7 +217,8 @@ class BulletedListPluginStyle extends ThemeExtension<BulletedListPluginStyle> {
color: Colors.white, color: Colors.white,
name: 'point', name: 'point',
); );
}); },
);
} }
class NumberListPluginStyle extends ThemeExtension<NumberListPluginStyle> { class NumberListPluginStyle extends ThemeExtension<NumberListPluginStyle> {
@ -264,7 +278,8 @@ class NumberListPluginStyle extends ThemeExtension<NumberListPluginStyle> {
}, },
); );
static final dark = light.copyWith(icon: (editorState, textNode) { static final dark = light.copyWith(
icon: (editorState, textNode) {
const iconPadding = EdgeInsets.only(left: 5.0, right: 5.0); const iconPadding = EdgeInsets.only(left: 5.0, right: 5.0);
return Container( return Container(
padding: iconPadding, padding: iconPadding,
@ -276,7 +291,8 @@ class NumberListPluginStyle extends ThemeExtension<NumberListPluginStyle> {
), ),
), ),
); );
}); },
);
} }
class QuotedTextPluginStyle extends ThemeExtension<QuotedTextPluginStyle> { class QuotedTextPluginStyle extends ThemeExtension<QuotedTextPluginStyle> {
@ -305,7 +321,9 @@ class QuotedTextPluginStyle extends ThemeExtension<QuotedTextPluginStyle> {
@override @override
ThemeExtension<QuotedTextPluginStyle> lerp( ThemeExtension<QuotedTextPluginStyle> lerp(
ThemeExtension<QuotedTextPluginStyle>? other, double t) { ThemeExtension<QuotedTextPluginStyle>? other,
double t,
) {
if (other is! QuotedTextPluginStyle) { if (other is! QuotedTextPluginStyle) {
return this; return this;
} }

View file

@ -14,7 +14,9 @@ import 'dart:io' show Platform;
import 'package:flutter/material.dart' hide Overlay, OverlayEntry; import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
typedef ToolbarItemEventHandler = void Function( typedef ToolbarItemEventHandler = void Function(
EditorState editorState, BuildContext context); EditorState editorState,
BuildContext context,
);
typedef ToolbarItemValidator = bool Function(EditorState editorState); typedef ToolbarItemValidator = bool Function(EditorState editorState);
typedef ToolbarItemHighlightCallback = bool Function(EditorState editorState); typedef ToolbarItemHighlightCallback = bool Function(EditorState editorState);
@ -413,7 +415,8 @@ void showLinkMenu(
); );
} }
_linkMenuOverlay = OverlayEntry(builder: (context) { _linkMenuOverlay = OverlayEntry(
builder: (context) {
return Positioned( return Positioned(
top: matchRect.bottom + 5.0, top: matchRect.bottom + 5.0,
left: matchRect.left, left: matchRect.left,
@ -457,7 +460,8 @@ void showLinkMenu(
), ),
), ),
); );
}); },
);
Overlay.of(context)?.insert(_linkMenuOverlay!); Overlay.of(context)?.insert(_linkMenuOverlay!);
editorState.service.scrollService?.disable(); editorState.service.scrollService?.disable();
@ -564,7 +568,8 @@ void showColorMenu(
} }
final style = editorState.editorStyle; final style = editorState.editorStyle;
_colorMenuOverlay = OverlayEntry(builder: (context) { _colorMenuOverlay = OverlayEntry(
builder: (context) {
return Positioned( return Positioned(
top: matchRect.bottom + 5.0, top: matchRect.bottom + 5.0,
left: matchRect.left + 10, left: matchRect.left + 10,
@ -575,11 +580,13 @@ void showColorMenu(
style.selectionMenuBackgroundColor ?? Colors.white, style.selectionMenuBackgroundColor ?? Colors.white,
pickerItemHoverColor: style.selectionMenuItemSelectedColor ?? pickerItemHoverColor: style.selectionMenuItemSelectedColor ??
Colors.blue.withOpacity(0.3), Colors.blue.withOpacity(0.3),
pickerItemTextColor: style.selectionMenuItemTextColor ?? Colors.black, pickerItemTextColor:
style.selectionMenuItemTextColor ?? Colors.black,
selectedFontColorHex: fontColorHex, selectedFontColorHex: fontColorHex,
selectedBackgroundColorHex: backgroundColorHex, selectedBackgroundColorHex: backgroundColorHex,
fontColorOptions: _generateFontColorOptions(editorState), fontColorOptions: _generateFontColorOptions(editorState),
backgroundColorOptions: _generateBackgroundColorOptions(editorState), backgroundColorOptions:
_generateBackgroundColorOptions(editorState),
onSubmittedbackgroundColorHex: (color) { onSubmittedbackgroundColorHex: (color) {
formatHighlightColor( formatHighlightColor(
editorState, editorState,
@ -597,7 +604,8 @@ void showColorMenu(
), ),
), ),
); );
}); },
);
Overlay.of(context)?.insert(_colorMenuOverlay!); Overlay.of(context)?.insert(_colorMenuOverlay!);
editorState.service.scrollService?.disable(); editorState.service.scrollService?.disable();

Some files were not shown because too many files have changed in this diff Show more