using System.Collections; using System.Collections.Generic; using System.Text; namespace Rokojori { public class TextLinesMapper { List _lines; int _cachedLineIndex; void Reset() { _lines = new List(); _cachedLineIndex = 0; } public void Map( string source ) { Reset(); var currentCharacterIndex = 0; currentCharacterIndex = NextLineBreak( source, currentCharacterIndex ); var lastCharacterIndex = 0; var lastLineBreakLength = 0; var currentLineIndex = 0; var maxTries = 10000; var currentTries = 0; // currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); } while ( currentCharacterIndex != -1 ) { currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); } var currentBreakLength = LineBreakLength( source, currentCharacterIndex ); var lineLength = currentCharacterIndex - lastCharacterIndex; var lineInfo = new TextLine( currentLineIndex, lastCharacterIndex, lineLength, lastLineBreakLength ); _lines.Add( lineInfo ); lastLineBreakLength = currentBreakLength; lastCharacterIndex = currentCharacterIndex; currentLineIndex ++; currentCharacterIndex = NextLineBreak( source, currentCharacterIndex + currentBreakLength ); } var endLineLength = source.Length - lastCharacterIndex; var endLineInfo = new TextLine( currentLineIndex, lastCharacterIndex, endLineLength, lastLineBreakLength ); _lines.Add( endLineInfo ); } public int numCharacters { get { return _lines[ _lines.Count -1 ].lineEnd + 1; } } public int numLines { get { return _lines.Count; } } public string lineInfos { get { var output = new List(); foreach ( var line in _lines ) { output.Add( line.info ); } return Lists.Join( output, ",\n" ); } } public TextLine GetLine( int characterIndex ) { var lineIndex = GetLineIndex( characterIndex ); return lineIndex == -1 ? null : _lines[ lineIndex ]; } public TextAnchor GetAnchor( int characterIndex, bool forTextEditor ) { var line = GetLine( characterIndex ); if ( line == null ) { return null; } return forTextEditor ? line.GetTextEditorAnchor( characterIndex ) : line.GetRawAnchor( characterIndex ); } public TextSelection GetSelection( int startCharacterIndex, int endCharacterIndex, bool forTextEditor = false ) { var startAnchor = GetAnchor( startCharacterIndex, forTextEditor ); var endAnchor = GetAnchor( endCharacterIndex, forTextEditor ); if ( startAnchor == null || endAnchor == null ) { return null; } return new TextSelection( startAnchor, endAnchor ); } public TextSelection GetTextEditorSelection( int startCharacterIndex, int endCharacterIndex ) { return GetSelection( startCharacterIndex, endCharacterIndex, true ); } public string GetTextEditorInfo( int startCharacterIndex, int endCharacterIndex ) { var selection = GetTextEditorSelection( startCharacterIndex, endCharacterIndex ); if ( selection != null ) { return selection.info; } return "@chars(" + startCharacterIndex + "," + endCharacterIndex + ")"; } public string GetTextEditorSnippet( string source, int startCharacterIndex, int endCharacterIndex ) { var startLine = GetLineIndex( startCharacterIndex ); var endLine = GetLineIndex( endCharacterIndex ); if ( endLine < startLine ) { var b = endLine; endLine = startLine; startLine = b; } var snippet = new StringBuilder(); var lengthMaxLineNumberIndex = ( _lines[ endLine ].textEditorLineIndex + "" ).Length; for ( var i = startLine; i < endLine; i++ ) { var line = _lines[ i ]; var lineNumber = _lines[ i ].textEditorLineIndex + ""; while ( lineNumber.Length < lengthMaxLineNumberIndex ) { lineNumber = "0" + lineNumber; } var content = line.GetContent( source ); snippet.Append( lineNumber + ": " ); snippet.Append( content ); snippet.Append( "\n" ); } return snippet.ToString(); } public string GetTextEditorInfo( int characterIndex ) { var anchor = GetAnchor( characterIndex, true ); if ( anchor != null ) { return anchor.info; } return "@chars(" + characterIndex + ")"; } TextLine _cachedLine { get { return _lines[ _cachedLineIndex ]; } } public int GetLineIndex( int characterIndex ) { if ( _cachedLine.Contains( characterIndex ) ) { return _cachedLineIndex; } if ( _lines.Count == 0 || characterIndex < 0 || characterIndex > numCharacters ) { return -1; } var lineContainsCharacterIndex = false; var lineIndexInRange = true; var characterIndexIsHigher = _cachedLine.characterIndex < characterIndex; var searchForward = 1; var searchBackward = -1 ; var searchStep = characterIndexIsHigher ? searchForward : searchBackward; var maxTries = 1000; var currentTries = 0; // currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); } do { currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); } var nextLineIndex = _cachedLineIndex + searchStep; lineIndexInRange = 0 <= nextLineIndex && nextLineIndex < _lines.Count; if ( lineIndexInRange ) { _cachedLineIndex = nextLineIndex; lineContainsCharacterIndex = _cachedLine.Contains( characterIndex ); } else { lineContainsCharacterIndex = false; } } while ( ! lineContainsCharacterIndex && lineIndexInRange ); if ( lineContainsCharacterIndex ) { return _cachedLineIndex; } return -1; } int NextLineBreak( string source, int offset ) { for ( int i = offset; i < source.Length; i++ ) { if ( source[ i ] == '\n' || source[ i ] == '\r' ) { return i; } } return -1; } int LineBreakLength( string source, int offset ) { if ( source[ offset ] == '\r' && ( ( offset + 1 ) < source.Length ) ) { return source[ offset + 1 ] == '\n' ? 2 : 1; } return 1; } } }