Skip to content

Commit 66ea89a

Browse files
authored
Merge pull request #199 from gui-cs/tig/tig-extract-automatic-folding-orchestrat
Extract automatic folding orchestration from Ted into Editor
2 parents f0868c9 + 5c1b661 commit 66ea89a

8 files changed

Lines changed: 646 additions & 270 deletions

File tree

examples/ted/TedApp.FileOperations.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,6 @@ private void ApplyFileMetadata (string? filePath)
586586

587587
UpdateFileNameShortcut ();
588588
UpdatePreviewVisibility ();
589-
InstallFolding ();
590589
Editor.SetNeedsDraw ();
591590
}
592591

examples/ted/TedApp.cs

Lines changed: 2 additions & 264 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,11 @@ namespace Ted;
2222
/// </summary>
2323
public sealed partial class TedApp : Window
2424
{
25-
private const int MaximumAutomaticFoldingDocumentLength = 1_000_000;
26-
27-
private readonly BraceFoldingStrategy _braceFoldingStrategy;
28-
2925
// Per-instance config path. Defaults to the real ~/.tui location; tests inject a temp path so they
3026
// never touch the developer's real config (and stay parallel-safe — no env/static mutation).
3127
private readonly string _configPath;
3228
private readonly Shortcut _fileNameShortcut;
3329
private readonly MenuItem _previewMarkdownMenuItem;
34-
private TextDocument? _foldingDocument;
35-
private bool _foldingUpdateNeeded;
3630

3731
/// <summary>Initializes a new <see cref="TedApp" />.</summary>
3832
/// <param name="readOnly">Opens the editor read-only.</param>
@@ -78,9 +72,8 @@ public TedApp (bool readOnly = false, string? configPath = null)
7872
Editor.IndentationStrategy =
7973
EditorSettings.AutoIndent ? new DefaultIndentationStrategy () : null;
8074

81-
// Enable brace-based folding. The strategy re-scans on each document change.
82-
_braceFoldingStrategy = new BraceFoldingStrategy ();
83-
InstallFolding ();
75+
// Enable brace-based folding. The editor handles the full lifecycle automatically.
76+
Editor.FoldingStrategy = new BraceFoldingStrategy ();
8477

8578
ShowOpenDialog = ShowDefaultOpenDialog;
8679
ShowSaveDialog = ShowDefaultSaveDialog;
@@ -573,259 +566,4 @@ private void ShowSettingsDialog ()
573566

574567
dialog.Dispose ();
575568
}
576-
577-
/// <summary>
578-
/// Creates a <see cref="FoldingManager" /> for the current document and wires up
579-
/// automatic fold updates on document changes.
580-
/// </summary>
581-
private void InstallFolding ()
582-
{
583-
if (Editor.Document is null)
584-
{
585-
SetFoldingDocument (null);
586-
587-
return;
588-
}
589-
590-
if (Editor.Document.TextLength > MaximumAutomaticFoldingDocumentLength)
591-
{
592-
SetFoldingDocument (null);
593-
Editor.FoldingManager = null;
594-
595-
return;
596-
}
597-
598-
FoldingManager fm = new (Editor.Document);
599-
Editor.FoldingManager = fm;
600-
_braceFoldingStrategy.UpdateFoldings (fm, Editor.Document);
601-
SetFoldingDocument (Editor.Document);
602-
}
603-
604-
private void UpdateFoldings ()
605-
{
606-
if (Editor.FoldingManager is null || Editor.Document is null)
607-
{
608-
return;
609-
}
610-
611-
if (Editor.Document.TextLength > MaximumAutomaticFoldingDocumentLength)
612-
{
613-
SetFoldingDocument (null);
614-
Editor.FoldingManager = null;
615-
616-
return;
617-
}
618-
619-
_braceFoldingStrategy.UpdateFoldings (Editor.FoldingManager,
620-
Editor.Document);
621-
}
622-
623-
private void SetFoldingDocument (TextDocument? document)
624-
{
625-
if (ReferenceEquals (_foldingDocument, document))
626-
{
627-
return;
628-
}
629-
630-
if (_foldingDocument is not null)
631-
{
632-
_foldingDocument.Changed -= OnFoldingDocumentChanged;
633-
_foldingDocument.UpdateFinished -= OnFoldingDocumentUpdateFinished;
634-
}
635-
636-
_foldingDocument = document;
637-
_foldingUpdateNeeded = false;
638-
639-
if (_foldingDocument is not null)
640-
{
641-
_foldingDocument.Changed += OnFoldingDocumentChanged;
642-
_foldingDocument.UpdateFinished += OnFoldingDocumentUpdateFinished;
643-
}
644-
}
645-
646-
private void OnFoldingDocumentChanged (object? sender, DocumentChangeEventArgs e)
647-
{
648-
_foldingUpdateNeeded |= FoldingChangeMayAffectStructure (e);
649-
}
650-
651-
private void OnFoldingDocumentUpdateFinished (object? sender, EventArgs e)
652-
{
653-
if (!_foldingUpdateNeeded)
654-
{
655-
return;
656-
}
657-
658-
_foldingUpdateNeeded = false;
659-
UpdateFoldings ();
660-
}
661-
662-
private bool FoldingChangeMayAffectStructure (DocumentChangeEventArgs e)
663-
{
664-
if (TryGetMappedStructuralChange (e, out var mappedStructuralChange))
665-
{
666-
return mappedStructuralChange;
667-
}
668-
669-
return ContainsFoldingStructuralCharacter (e.InsertedText)
670-
|| ContainsFoldingStructuralCharacter (e.RemovedText);
671-
}
672-
673-
private bool TryGetMappedStructuralChange (DocumentChangeEventArgs e, out bool structuralChange)
674-
{
675-
structuralChange = false;
676-
OffsetChangeMap map = e.OffsetChangeMap;
677-
var hasInsertion = false;
678-
var hasRemoval = false;
679-
680-
if (map.Count == 0)
681-
{
682-
return false;
683-
}
684-
685-
foreach (OffsetChangeMapEntry entry in map)
686-
{
687-
hasInsertion |= entry.InsertionLength > 0;
688-
hasRemoval |= entry.RemovalLength > 0;
689-
690-
if (entry.InsertionLength > 0 && entry.RemovalLength > 0)
691-
{
692-
return false;
693-
}
694-
}
695-
696-
if (!hasInsertion && !hasRemoval)
697-
{
698-
return false;
699-
}
700-
701-
if (hasInsertion && hasRemoval)
702-
{
703-
return false;
704-
}
705-
706-
structuralChange = hasInsertion
707-
? MappedInsertionsContainFoldingStructuralCharacter (map, e.InsertedText, e.Offset)
708-
: MappedRemovalsContainFoldingStructuralCharacter (map, e.RemovedText, e.Offset, e.RemovalLength);
709-
710-
return true;
711-
}
712-
713-
private bool MappedInsertionsContainFoldingStructuralCharacter (OffsetChangeMap map, ITextSource text,
714-
int baseOffset)
715-
{
716-
if (InsertionEntriesUseInsertedTextCoordinates (map, baseOffset, text.TextLength))
717-
{
718-
return MappedInsertionsContainFoldingStructuralCharacterWithoutShift (map, text, baseOffset);
719-
}
720-
721-
var insertedShift = 0;
722-
723-
foreach (OffsetChangeMapEntry entry in map)
724-
{
725-
if (entry.InsertionLength == 0)
726-
{
727-
continue;
728-
}
729-
730-
var relativeOffset = entry.Offset - baseOffset + insertedShift;
731-
732-
if (ContainsFoldingStructuralCharacter (text, relativeOffset, entry.InsertionLength))
733-
{
734-
return true;
735-
}
736-
737-
insertedShift += entry.InsertionLength;
738-
}
739-
740-
return false;
741-
}
742-
743-
private bool MappedInsertionsContainFoldingStructuralCharacterWithoutShift (
744-
OffsetChangeMap map,
745-
ITextSource text,
746-
int baseOffset)
747-
{
748-
foreach (OffsetChangeMapEntry entry in map)
749-
{
750-
if (entry.InsertionLength == 0)
751-
{
752-
continue;
753-
}
754-
755-
var relativeOffset = entry.Offset - baseOffset;
756-
757-
if (ContainsFoldingStructuralCharacter (text, relativeOffset, entry.InsertionLength))
758-
{
759-
return true;
760-
}
761-
}
762-
763-
return false;
764-
}
765-
766-
private bool MappedRemovalsContainFoldingStructuralCharacter (
767-
OffsetChangeMap map,
768-
ITextSource text,
769-
int baseOffset,
770-
int removalLength)
771-
{
772-
if (!RemovalEntriesUseRemovedTextCoordinates (map, baseOffset, removalLength))
773-
{
774-
return MappedInsertionsContainFoldingStructuralCharacter (map.Invert (), text, baseOffset);
775-
}
776-
777-
foreach (OffsetChangeMapEntry entry in map)
778-
{
779-
if (entry.RemovalLength == 0)
780-
{
781-
continue;
782-
}
783-
784-
var relativeOffset = entry.Offset - baseOffset;
785-
786-
if (ContainsFoldingStructuralCharacter (text, relativeOffset, entry.RemovalLength))
787-
{
788-
return true;
789-
}
790-
}
791-
792-
return false;
793-
}
794-
795-
private static bool RemovalEntriesUseRemovedTextCoordinates (OffsetChangeMap map, int baseOffset, int removalLength)
796-
{
797-
return map.Count > 0
798-
&& map[0].RemovalLength > 0
799-
&& map[0].Offset + map[0].RemovalLength == baseOffset + removalLength;
800-
}
801-
802-
private static bool InsertionEntriesUseInsertedTextCoordinates (OffsetChangeMap map, int baseOffset,
803-
int insertionLength)
804-
{
805-
return map.Count > 0
806-
&& map[^1].InsertionLength > 0
807-
&& map[^1].Offset + map[^1].InsertionLength == baseOffset + insertionLength;
808-
}
809-
810-
private bool ContainsFoldingStructuralCharacter (ITextSource text)
811-
{
812-
return ContainsFoldingStructuralCharacter (text, 0, text.TextLength);
813-
}
814-
815-
private bool ContainsFoldingStructuralCharacter (ITextSource text, int offset, int length)
816-
{
817-
for (var i = offset; i < offset + length; i++)
818-
{
819-
var ch = text.GetCharAt (i);
820-
821-
if (ch == _braceFoldingStrategy.OpeningBrace
822-
|| ch == _braceFoldingStrategy.ClosingBrace
823-
|| ch is '\r' or '\n')
824-
{
825-
return true;
826-
}
827-
}
828-
829-
return false;
830-
}
831569
}

0 commit comments

Comments
 (0)