list button implementation
This commit is contained in:
parent
8c0c478847
commit
3c274adee7
|
@ -1,16 +1,25 @@
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
extension on String {
|
extension Utilities on String {
|
||||||
int getBeginningOfPreviousLine(int startingIndex) {
|
int getBeginningOfTheLine(int startingIndex) {
|
||||||
|
if (startingIndex <= 0) return 0;
|
||||||
for (var i = startingIndex; i >= 0; i--) {
|
for (var i = startingIndex; i >= 0; i--) {
|
||||||
if (this[i] == '\n') return i + 1;
|
if (this[i] == '\n') return i + 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getEndOfTheLine(int from) {
|
||||||
|
for (var i = from; i < length; i++) {
|
||||||
|
if (this[i] == '\n') return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
// returns the line that ends at endingIndex
|
// returns the line that ends at endingIndex
|
||||||
String lineBefore(int endingIndex) {
|
String lineBefore(int endingIndex) {
|
||||||
return substring(getBeginningOfPreviousLine(endingIndex), endingIndex + 1);
|
return substring(getBeginningOfTheLine(endingIndex), endingIndex + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// utililty class for traversing through multiline text
|
||||||
|
class TextLinesIterator extends Iterator {
|
||||||
|
String text;
|
||||||
|
int beg;
|
||||||
|
int end;
|
||||||
|
TextSelection? selection;
|
||||||
|
|
||||||
|
TextLinesIterator(this.text, {this.selection})
|
||||||
|
: end = -1,
|
||||||
|
beg = -1;
|
||||||
|
|
||||||
|
factory TextLinesIterator.fromController(TextEditingController controller) =>
|
||||||
|
TextLinesIterator(controller.text, selection: controller.selection);
|
||||||
|
|
||||||
|
bool get isWithinSelection {
|
||||||
|
final selection = this.selection;
|
||||||
|
if (selection == null || beg == -1) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return (selection.end >= beg && beg >= selection.start) ||
|
||||||
|
(selection.end >= end && end >= selection.start) ||
|
||||||
|
(end >= selection.start && selection.start >= beg) ||
|
||||||
|
(end >= selection.end && selection.end >= beg) ||
|
||||||
|
(beg <= selection.start &&
|
||||||
|
selection.start <= end &&
|
||||||
|
beg <= selection.end &&
|
||||||
|
selection.end <= end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get current {
|
||||||
|
return text.substring(beg, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
set current(String newVal) {
|
||||||
|
final selected = isWithinSelection;
|
||||||
|
text = text.replaceRange(beg, end, newVal);
|
||||||
|
final wordLen = end - beg;
|
||||||
|
final dif = newVal.length - wordLen;
|
||||||
|
end += dif;
|
||||||
|
|
||||||
|
final selection = this.selection;
|
||||||
|
if (selection == null) return;
|
||||||
|
|
||||||
|
if (selected || selection.baseOffset > end) {
|
||||||
|
this.selection =
|
||||||
|
selection.copyWith(extentOffset: selection.extentOffset + dif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
end = -1;
|
||||||
|
beg = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool moveNext() {
|
||||||
|
if (end == text.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (beg == -1) {
|
||||||
|
end = 0;
|
||||||
|
beg = 0;
|
||||||
|
} else {
|
||||||
|
end += 1;
|
||||||
|
beg = end;
|
||||||
|
}
|
||||||
|
for (; end < text.length; end++) {
|
||||||
|
if (text[end] == '\n') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end = text.length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the lines as a list but also moves the pointer to the back
|
||||||
|
List<String> get asList {
|
||||||
|
reset();
|
||||||
|
final list = <String>[];
|
||||||
|
while (moveNext()) {
|
||||||
|
list.add(current);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
import '../../formatter.dart';
|
||||||
import '../../util/extensions/spaced.dart';
|
import '../../util/extensions/spaced.dart';
|
||||||
|
import '../../util/text_lines_iterator.dart';
|
||||||
|
|
||||||
class Reformat {
|
class Reformat {
|
||||||
final String text;
|
final String text;
|
||||||
|
@ -43,6 +45,54 @@ extension on TextEditingController {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get firstSelectedLine {
|
||||||
|
if (text.isEmpty) return '';
|
||||||
|
return text.substring(text.getBeginningOfTheLine(selection.start - 1),
|
||||||
|
text.getEndOfTheLine(selection.end));
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeAtBeginningOfEverySelectedLine(String s) {
|
||||||
|
final lines = TextLinesIterator.fromController(this);
|
||||||
|
var linesCount = 0;
|
||||||
|
while (lines.moveNext()) {
|
||||||
|
if (lines.isWithinSelection) {
|
||||||
|
if (lines.current.startsWith(RegExp.escape(s))) {
|
||||||
|
lines.current = lines.current.substring(s.length);
|
||||||
|
linesCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.copyWith(
|
||||||
|
text: lines.text,
|
||||||
|
selection: selection.copyWith(
|
||||||
|
baseOffset: selection.baseOffset - s.length,
|
||||||
|
extentOffset: selection.extentOffset - s.length * linesCount,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertAtBeginningOfEverySelectedLine(String s) {
|
||||||
|
final lines = TextLinesIterator.fromController(this);
|
||||||
|
var linesCount = 0;
|
||||||
|
while (lines.moveNext()) {
|
||||||
|
if (lines.isWithinSelection) {
|
||||||
|
if (!lines.current.startsWith(RegExp.escape(s))) {
|
||||||
|
lines.current = '$s${lines.current}';
|
||||||
|
linesCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.copyWith(
|
||||||
|
text: lines.text,
|
||||||
|
selection: selection.copyWith(
|
||||||
|
baseOffset: selection.baseOffset + s.length,
|
||||||
|
extentOffset: selection.extentOffset + s.length * linesCount,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void reformat(Reformat Function(String selection) reformatter) {
|
void reformat(Reformat Function(String selection) reformatter) {
|
||||||
final beg = beforeSelectionText;
|
final beg = beforeSelectionText;
|
||||||
final mid = selectionText;
|
final mid = selectionText;
|
||||||
|
@ -101,9 +151,7 @@ class Toolbar extends HookWidget {
|
||||||
icon: const Icon(Icons.person),
|
icon: const Icon(Icons.person),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {},
|
||||||
//
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.home),
|
icon: const Icon(Icons.home),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -119,7 +167,17 @@ class Toolbar extends HookWidget {
|
||||||
icon: const Icon(Icons.format_quote),
|
icon: const Icon(Icons.format_quote),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
final line = controller.firstSelectedLine;
|
||||||
|
|
||||||
|
if (line.startsWith(RegExp.escape('* '))) {
|
||||||
|
controller.removeAtBeginningOfEverySelectedLine('* ');
|
||||||
|
} else if (line.startsWith('- ')) {
|
||||||
|
controller.removeAtBeginningOfEverySelectedLine('- ');
|
||||||
|
} else {
|
||||||
|
controller.insertAtBeginningOfEverySelectedLine('- ');
|
||||||
|
}
|
||||||
|
},
|
||||||
icon: const Icon(Icons.format_list_bulleted)),
|
icon: const Icon(Icons.format_list_bulleted)),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => controller.surround('`'),
|
onPressed: () => controller.surround('`'),
|
||||||
|
|
Loading…
Reference in New Issue