233 lines
6.7 KiB
C#
233 lines
6.7 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace Rokojori
|
|
{
|
|
public class TextLinesMapper
|
|
{
|
|
List<TextLine> _lines;
|
|
int _cachedLineIndex;
|
|
|
|
void Reset()
|
|
{
|
|
_lines = new List<TextLine>();
|
|
_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<string>();
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
} |