Просмотр исходного кода

Prepares TerminalSearch to have multiple search tasks

by caching the complete search string when possible so that every search task can use the same cached version of the combined search string.
Currently there is the user search task. In the future more search tasks (like searching for URLS) can be added
devmil 4 лет назад
Родитель
Сommit
406fde0727
3 измененных файлов с 118 добавлено и 56 удалено
  1. 1 0
      lib/buffer/line/line.dart
  2. 7 13
      lib/terminal/terminal.dart
  3. 110 43
      lib/terminal/terminal_search.dart

+ 1 - 0
lib/buffer/line/line.dart

@@ -145,6 +145,7 @@ class BufferLine {
   }
 
   String? _searchStringCache;
+  bool get hasCachedSearchString => _searchStringCache != null;
 
   String toSearchString(int cols) {
     if (_searchStringCache != null) {

+ 7 - 13
lib/terminal/terminal.dart

@@ -44,11 +44,11 @@ class Terminal with Observable implements TerminalUiInteraction {
     this.theme = TerminalThemes.defaultTheme,
     required int maxLines,
   }) : _maxLines = maxLines {
-    _search = TerminalSearch(
-      this,
-      (line) => line.markSearchDone(),
-      (line) => line.isSearchDirty,
-    );
+    _search = TerminalSearch(this);
+    _userSearchTask = _search.createSearchTask(
+        (line) => line.markSearchDone(), (line) => line.isSearchDirty);
+    //TODO: remove and tie to the API
+    _userSearchTask.pattern = "test";
     backend?.init();
     backend?.exitCode.then((value) {
       _isTerminated = true;
@@ -69,6 +69,7 @@ class Terminal with Observable implements TerminalUiInteraction {
   }
 
   late TerminalSearch _search;
+  late TerminalSearchTask _userSearchTask;
 
   bool _dirty = false;
   @override
@@ -741,12 +742,5 @@ class Terminal with Observable implements TerminalUiInteraction {
     refresh();
   }
 
-  String? _searchPattern = "test";
-
-  TerminalSearchResult get searchHits {
-    if (_searchPattern == null) {
-      return TerminalSearchResult.empty();
-    }
-    return _search.doSearch(_searchPattern!);
-  }
+  TerminalSearchResult get searchHits => _userSearchTask.searchResult;
 }

+ 110 - 43
lib/terminal/terminal_search.dart

@@ -1,3 +1,4 @@
+import 'package:meta/meta.dart';
 import 'package:xterm/buffer/line/line.dart';
 import 'package:xterm/terminal/terminal.dart';
 
@@ -66,63 +67,78 @@ class TerminalSearchHit {
 typedef MarkSearchDoneFunc = void Function(BufferLine line);
 typedef IsSearchDirtyFunc = bool Function(BufferLine line);
 
-class TerminalSearch {
-  TerminalSearch(
-      this._terminal, this._markSearchDoneFunc, this._isSearchDirtyFunc);
+class TerminalSearchTask {
+  TerminalSearchTask(this._search, this._terminal, this._markSearchDoneFunc,
+      this._isSearchDirtyFunc);
 
+  final TerminalSearch _search;
   final Terminal _terminal;
+  String? _pattern = null;
+  bool _isPatternDirty = true;
+  RegExp? _searchRegexp = null;
   final MarkSearchDoneFunc _markSearchDoneFunc;
   final IsSearchDirtyFunc _isSearchDirtyFunc;
 
-  String? _lastSearchPattern = null;
-  TerminalSearchResult? _lastSearchResult = null;
   bool? _hasBeenUsingAltBuffer;
+  TerminalSearchResult? _lastSearchResult = null;
 
-  TerminalSearchResult doSearch(String searchPattern) {
+  bool _isAnyLineDirty() {
     final bufferLength = _terminal.buffer.lines.length;
-    final terminalWidth = _terminal.terminalWidth;
-
-    var isSearchDirty = false;
-    //check if the search is dirty and return if not
-    if (_lastSearchPattern != null &&
-        _lastSearchPattern == searchPattern &&
-        _lastSearchResult != null &&
-        _hasBeenUsingAltBuffer != null &&
-        _hasBeenUsingAltBuffer! == _terminal.isUsingAltBuffer()) {
-      for (var i = 0; i < bufferLength; i++) {
-        if (_isSearchDirtyFunc(_terminal.buffer.lines[i])) {
-          isSearchDirty = true;
-          break;
-        }
+    for (var i = 0; i < bufferLength; i++) {
+      if (_isSearchDirtyFunc(_terminal.buffer.lines[i])) {
+        return true;
       }
-    } else {
-      isSearchDirty = true;
     }
+    return false;
+  }
 
-    if (!isSearchDirty) {
-      return _lastSearchResult!;
+  bool _isTerminalStateDirty() {
+    if (_isAnyLineDirty()) {
+      return true;
+    }
+    if (_hasBeenUsingAltBuffer != null &&
+        _hasBeenUsingAltBuffer! != _terminal.isUsingAltBuffer()) {
+      return true;
     }
+    return false;
+  }
 
-    //TODO: make caseSensitive an option
-    final searchRegex =
-        RegExp(searchPattern, caseSensitive: false, multiLine: false);
+  bool get isDirty {
+    if (_isPatternDirty) {
+      return true;
+    }
+    return _isTerminalStateDirty();
+  }
 
-    final result = List<TerminalSearchHit>.empty(growable: true);
+  String? get pattern => _pattern;
+  void set pattern(String? newPattern) {
+    if (newPattern != _pattern) {
+      _pattern = newPattern;
+      _isPatternDirty = true;
+      _searchRegexp = null;
+    }
+  }
 
-    final bufferContent = StringBuffer();
-    for (var i = 0; i < bufferLength; i++) {
-      final BufferLine line = _terminal.buffer.lines[i];
-      final searchString = line.toSearchString(terminalWidth);
-      _markSearchDoneFunc(line);
-      bufferContent.write(searchString);
-      if (searchString.length < terminalWidth) {
-        // fill up so that the row / col can be mapped back later on
-        bufferContent.writeAll(
-            List<String>.filled(terminalWidth - searchString.length, ' '));
-      }
+  TerminalSearchResult get searchResult {
+    if (_pattern == null) {
+      return TerminalSearchResult.empty();
     }
+    if (_lastSearchResult != null && !isDirty) {
+      return _lastSearchResult!;
+    }
+    _isPatternDirty = false;
+
+    final terminalWidth = _terminal.terminalWidth;
 
-    for (final match in searchRegex.allMatches(bufferContent.toString())) {
+    //TODO: make caseSensitive an option
+    if (_searchRegexp == null) {
+      _searchRegexp = RegExp(_pattern!, caseSensitive: false, multiLine: false);
+    }
+
+    final hits = List<TerminalSearchHit>.empty(growable: true);
+
+    for (final match
+        in _searchRegexp!.allMatches(_search.terminalSearchString)) {
       final startLineIndex = (match.start / terminalWidth).floor();
       final endLineIndex = (match.end / terminalWidth).floor();
 
@@ -130,7 +146,7 @@ class TerminalSearch {
       final startIndex = match.start - startLineIndex * terminalWidth;
       final endIndex = match.end - endLineIndex * terminalWidth;
 
-      result.add(
+      hits.add(
         TerminalSearchHit(
           startLineIndex,
           startIndex,
@@ -139,9 +155,60 @@ class TerminalSearch {
         ),
       );
     }
-    _lastSearchPattern = searchPattern;
-    _lastSearchResult = TerminalSearchResult.fromHits(result);
+
+    _lastSearchResult = TerminalSearchResult.fromHits(hits);
     _hasBeenUsingAltBuffer = _terminal.isUsingAltBuffer();
     return _lastSearchResult!;
   }
 }
+
+class TerminalSearch {
+  TerminalSearch(this._terminal);
+
+  final Terminal _terminal;
+  String? _cachedSearchString;
+  int? _lastTerminalWidth;
+
+  TerminalSearchTask createSearchTask(MarkSearchDoneFunc markSearchDoneFunc,
+      IsSearchDirtyFunc isSearchDirtyFunc) {
+    return TerminalSearchTask(
+        this, _terminal, markSearchDoneFunc, isSearchDirtyFunc);
+  }
+
+  String get terminalSearchString {
+    final bufferLength = _terminal.buffer.lines.length;
+    final terminalWidth = _terminal.terminalWidth;
+
+    var isAnySearchStringInvalid = false;
+    for (var i = 0; i < bufferLength; i++) {
+      if (!_terminal.buffer.lines[i].hasCachedSearchString) {
+        isAnySearchStringInvalid = true;
+      }
+    }
+
+    late String completeSearchString;
+    if (_cachedSearchString != null &&
+        _lastTerminalWidth != null &&
+        _lastTerminalWidth! == terminalWidth &&
+        !isAnySearchStringInvalid) {
+      completeSearchString = _cachedSearchString!;
+    } else {
+      final bufferContent = StringBuffer();
+      for (var i = 0; i < bufferLength; i++) {
+        final BufferLine line = _terminal.buffer.lines[i];
+        final searchString = line.toSearchString(terminalWidth);
+        bufferContent.write(searchString);
+        if (searchString.length < terminalWidth) {
+          // fill up so that the row / col can be mapped back later on
+          bufferContent.writeAll(
+              List<String>.filled(terminalWidth - searchString.length, ' '));
+        }
+      }
+      completeSearchString = bufferContent.toString();
+      _cachedSearchString = completeSearchString;
+      _lastTerminalWidth = terminalWidth;
+    }
+
+    return completeSearchString;
+  }
+}