]> O.S.I.I.S - jp/crowedit.git/commitdiff
tabulation handling
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Thu, 31 Aug 2017 10:55:06 +0000 (12:55 +0200)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Thu, 31 Aug 2017 11:12:48 +0000 (13:12 +0200)
src/CodeBuffer.cs
src/SourceEditor.cs
src/Token.cs

index 104ddb4d958580d46217da8ae0bdbcc922ed7134..3c82d12f74ed4241d14972a8bdfca0e52be8e72d 100644 (file)
@@ -42,9 +42,21 @@ namespace Crow.Coding
                List<string> lines = new List<string>();
                public int longestLineIdx = 0;
                public int longestLineCharCount = 0;
+               /// <summary>
+               /// real position in char arrays, tab = 1 char
+               /// </summary>
+               int _currentLine = 0;
+               int _currentCol = 0;
 
                public int Length { get { return lines.Count;}}
 
+               /// <summary>
+               /// Return line with tabs replaced by spaces
+               /// </summary>
+               public string GetPrintableLine(int i){
+                       return string.IsNullOrEmpty(lines[i]) ? "" : lines[i].Replace("\t", new String(' ', Interface.TabSize));
+               }
+
                public string this[int i]
                {
                        get { return lines[i]; }
@@ -83,14 +95,14 @@ namespace Crow.Coding
                        AddRange (Regex.Split (rawSource, "\r\n|\r|\n|\\\\n"));
 
                        lineBreak = detectLineBreakKind (rawSource);
-                       findLongestLine ();
+                       findLongestVisualLine ();
                }
 
-               void findLongestLine(){
+               void findLongestVisualLine(){
                        longestLineCharCount = 0;
-                       for (int i = 0; i < lines.Count; i++) {
-                               if (lines[i].Length > longestLineCharCount) {
-                                       longestLineCharCount = lines[i].Length;
+                       for (int i = 0; i < this.Length; i++) {
+                               if (this.GetPrintableLine(i).Length > longestLineCharCount) {
+                                       longestLineCharCount = this.GetPrintableLine(i).Length;
                                        longestLineIdx = i;
                                }
                        }
@@ -127,6 +139,207 @@ namespace Crow.Coding
                                return lines.Count > 0 ? lines.Aggregate((i, j) => i + this.lineBreak + j) : "";
                        }
                }
+
+               /// <summary>
+               /// convert visual position to buffer position
+               /// </summary>
+               Point getBuffPos (Point visualPos) {
+                       int i = 0;
+                       int buffCol = 0;
+                       while (i < visualPos.X) {
+                               if (this [visualPos.Y] [buffCol] == '\t')
+                                       i += Interface.TabSize;
+                               else
+                                       i++;
+                               buffCol++;
+                       }
+                       return new Point (buffCol, visualPos.Y);
+               }
+               /// <summary>
+               /// convert buffer postition to visual position
+               /// </summary>
+               Point getVisualPos (Point buffPos) {
+                       int vCol = this[buffPos.Y].Substring(0, buffPos.X).Replace("\t", new String(' ', Interface.TabSize)).Length;
+                       return new Point (vCol, buffPos.Y);
+               }
+               /// <summary>
+               /// Gets visual position computed from actual buffer position
+               /// </summary>
+               public Point VisualPosition {
+                       get { return getVisualPos (new Point (_currentCol, _currentLine)); }
+               }
+               /// <summary>
+               /// set buffer current position from visual position
+               /// </summary>
+               public void SetBufferPos(Point visualPosition) {
+                       CurrentPosition = getBuffPos(visualPosition);
+               }
+
+               #region Editing and moving cursor
+               Point selectionStart = -1;
+               Point selectionEnd = -1;
+               public void SetSelection (Point visualStart, Point visualEnd) {
+                       selectionStart = getBuffPos (visualStart);
+                       selectionEnd = getBuffPos (visualEnd);
+               }
+               public void ResetSelection () {
+                       selectionStart = selectionEnd = -1;
+               }
+               bool selectionIsEmpty {
+                       get { return selectionStart == selectionEnd; }
+               }
+               public int CurrentColumn{
+                       get { return _currentCol; }
+                       set {
+                               if (value == _currentCol)
+                                       return;
+                               if (value < 0)
+                                       _currentCol = 0;
+                               else if (value >  lines [_currentLine].Length)
+                                       _currentCol = lines [_currentLine].Length;
+                               else
+                                       _currentCol = value;
+                       }
+               }
+               public int CurrentLine{
+                       get { return _currentLine; }
+                       set {
+                               if (value == _currentLine)
+                                       return;
+                               if (value >= lines.Count)
+                                       _currentLine = lines.Count-1;
+                               else if (value < 0)
+                                       _currentLine = 0;
+                               else
+                                       _currentLine = value;
+                               //force recheck of currentCol for bounding
+                               int cc = _currentCol;
+                               _currentCol = 0;
+                               CurrentColumn = cc;
+                       }
+               }
+               public Point CurrentPosition {
+                       get { return new Point(CurrentColumn, CurrentLine); }
+                       set {
+                               _currentCol = value.X;
+                               _currentLine = value.Y;
+                       }
+               }
+               protected Char CurrentChar { get { return lines [CurrentLine] [CurrentColumn]; } }
+
+               /// <summary>
+               /// Moves cursor one char to the left.
+               /// </summary>
+               /// <returns><c>true</c> if move succeed</returns>
+               public bool MoveLeft(){
+                       int tmp = _currentCol - 1;
+                       if (tmp < 0) {
+                               if (_currentLine == 0)
+                                       return false;
+                               CurrentLine--;
+                               CurrentColumn = int.MaxValue;
+                       } else
+                               CurrentColumn = tmp;
+                       return true;
+               }
+               /// <summary>
+               /// Moves cursor one char to the right.
+               /// </summary>
+               /// <returns><c>true</c> if move succeed</returns>
+               public bool MoveRight(){
+                       int tmp = _currentCol + 1;
+                       if (tmp > this [_currentLine].Length){
+                               if (CurrentLine == this.Length - 1)
+                                       return false;
+                               CurrentLine++;
+                               CurrentColumn = 0;
+                       } else
+                               CurrentColumn = tmp;
+                       return true;
+               }
+               public void GotoWordStart(){
+                       if (this[CurrentLine].Length == 0)
+                               return;
+                       CurrentColumn--;
+                       //skip white spaces
+                       while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
+                               CurrentColumn--;
+                       while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
+                               CurrentColumn--;
+                       if (!char.IsLetterOrDigit (this.CurrentChar))
+                               CurrentColumn++;
+               }
+               public void GotoWordEnd(){
+                       //skip white spaces
+                       if (CurrentColumn >= this [CurrentLine].Length - 1)
+                               return;
+                       while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1)
+                               CurrentColumn++;
+                       while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1)
+                               CurrentColumn++;
+                       if (char.IsLetterOrDigit (this.CurrentChar))
+                               CurrentColumn++;
+               }
+               public void DeleteChar()
+               {
+                       if (selectionIsEmpty) {
+                               if (CurrentColumn == 0) {
+                                       if (CurrentLine == 0 && this.Length == 1)
+                                               return;
+                                       CurrentLine--;
+                                       CurrentColumn = this [CurrentLine].Length;
+                                       this [CurrentLine] += this [CurrentLine + 1];
+                                       RemoveAt (CurrentLine + 1);
+                                       return;
+                               }
+                               CurrentColumn--;
+                               this [CurrentLine] = this [CurrentLine].Remove (CurrentColumn, 1);
+                       } else {
+                               int linesToRemove = selectionEnd.Y - selectionStart.Y + 1;
+                               int l = selectionStart.Y;
+
+                               if (linesToRemove > 0) {
+                                       this [l] = this [l].Remove (selectionStart.X, this [l].Length - selectionStart.X) +
+                                               this [selectionEnd.Y].Substring (selectionEnd.X, this [selectionEnd.Y].Length - selectionEnd.X);
+                                       l++;
+                                       for (int c = 0; c < linesToRemove-1; c++)
+                                               RemoveAt (l);
+                                       CurrentLine = selectionStart.Y;
+                                       CurrentColumn = selectionStart.X;
+                               } else
+                                       this [l] = this [l].Remove (selectionStart.X, selectionEnd.X - selectionStart.X);
+                               CurrentColumn = selectionStart.X;
+                               ResetSelection ();
+                       }
+               }
+               /// <summary>
+               /// Insert new string at caret position, should be sure no line break is inside.
+               /// </summary>
+               /// <param name="str">String.</param>
+               public void Insert(string str)
+               {
+                       if (!selectionIsEmpty)
+                               this.DeleteChar ();
+                       string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray();
+                       this [CurrentLine] = this [CurrentLine].Insert (CurrentColumn, strLines[0]);
+                       CurrentColumn += strLines[0].Length;
+                       for (int i = 1; i < strLines.Length; i++) {
+                               InsertLineBreak ();
+                               this [CurrentLine] = this [CurrentLine].Insert (CurrentColumn, strLines[i]);
+                               CurrentColumn += strLines[i].Length;
+                       }
+               }
+               /// <summary>
+               /// Insert a line break.
+               /// </summary>
+               public void InsertLineBreak()
+               {
+                       Insert(CurrentLine + 1, this[CurrentLine].Substring(CurrentColumn));
+                       this [CurrentLine] = this [CurrentLine].Substring (0, CurrentColumn);
+                       CurrentLine++;
+                       CurrentColumn = 0;
+               }
+               #endregion
        }
 }
 
index 7b73819fd83fb2689606f3088fbdd712a9426d81..237a5d87730bbd3b95af7695acf12a3dd5f7dacd 100644 (file)
@@ -192,11 +192,13 @@ namespace Crow.Coding
                                        return;
                                if (value < 0)
                                        _currentCol = 0;
-                               else if (value > buffer [_currentLine].Length)
-                                       _currentCol = buffer [_currentLine].Length;
+                               else if (value > buffer.GetPrintableLine(_currentLine).Length)
+                                       _currentCol = buffer.GetPrintableLine(_currentLine).Length;
                                else
                                        _currentCol = value;
 
+                               buffer.SetBufferPos (CurrentPosition);
+
                                if (_currentCol < ScrollX)
                                        ScrollX = _currentCol;
                                else if (_currentCol >= ScrollX + visibleColumns)
@@ -221,6 +223,9 @@ namespace Crow.Coding
                                int cc = _currentCol;
                                _currentCol = 0;
                                CurrentColumn = cc;
+
+                               buffer.SetBufferPos (CurrentPosition);
+
                                //System.Diagnostics.Debug.WriteLine ("Scroll:{0} visibleLines:{1} CurLine:{2}", ScrollY, visibleLines, CurrentLine);
                                if (_currentLine < ScrollY)
                                        ScrollY = _currentLine;
@@ -231,6 +236,10 @@ namespace Crow.Coding
                }
                [XmlIgnore]public Point CurrentPosition {
                        get { return new Point(CurrentColumn, CurrentLine); }
+                       set {
+                               CurrentColumn = value.X;
+                               CurrentLine = value.Y;
+                       }
                }
                //TODO:using HasFocus for drawing selection cause SelBegin and Release binding not to work
                /// <summary>
@@ -319,87 +328,35 @@ namespace Crow.Coding
                /// </summary>
                /// <returns><c>true</c> if move succeed</returns>
                public bool MoveLeft(){
-                       int tmp = _currentCol - 1;
-                       if (tmp < 0) {
-                               if (_currentLine == 0)
-                                       return false;
-                               CurrentLine--;
-                               CurrentColumn = int.MaxValue;
-                       } else
-                               CurrentColumn = tmp;
-                       return true;
+                       bool res = buffer.MoveLeft();
+                       CurrentPosition = buffer.VisualPosition;
+                       return res;
                }
                /// <summary>
                /// Moves cursor one char to the right.
                /// </summary>
                /// <returns><c>true</c> if move succeed</returns>
                public bool MoveRight(){
-                       int tmp = _currentCol + 1;
-                       if (tmp > buffer [_currentLine].Length){
-                               if (CurrentLine == buffer.Length - 1)
-                                       return false;
-                               CurrentLine++;
-                               CurrentColumn = 0;
-                       } else
-                               CurrentColumn = tmp;
-                       return true;
+                       bool res = buffer.MoveRight();
+                       CurrentPosition = buffer.VisualPosition;
+                       return res;
                }
                public void GotoWordStart(){
-                       if (buffer[CurrentLine].Length == 0)
-                               return;
-                       CurrentColumn--;
-                       //skip white spaces
-                       while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
-                               CurrentColumn--;
-                       while (char.IsLetterOrDigit (buffer [CurrentLine] [CurrentColumn]) && CurrentColumn > 0)
-                               CurrentColumn--;
-                       if (!char.IsLetterOrDigit (this.CurrentChar))
-                               CurrentColumn++;
+                       buffer.GotoWordStart();
+                       CurrentPosition = buffer.VisualPosition;
                }
                public void GotoWordEnd(){
-                       //skip white spaces
-                       if (CurrentColumn >= buffer [CurrentLine].Length - 1)
-                               return;
-                       while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < buffer [CurrentLine].Length-1)
-                               CurrentColumn++;
-                       while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < buffer [CurrentLine].Length-1)
-                               CurrentColumn++;
-                       if (char.IsLetterOrDigit (this.CurrentChar))
-                               CurrentColumn++;
+                       buffer.GotoWordEnd();
+                       CurrentPosition = buffer.VisualPosition;
                }
                public void DeleteChar()
                {
-                       if (selectionIsEmpty) {
-                               if (CurrentColumn == 0) {
-                                       if (CurrentLine == 0 && buffer.Length == 1)
-                                               return;
-                                       CurrentLine--;
-                                       CurrentColumn = buffer [CurrentLine].Length;
-                                       buffer [CurrentLine] += buffer [CurrentLine + 1];
-                                       buffer.RemoveAt (CurrentLine + 1);
-                                       OnTextChanged (this, null);
-                                       return;
-                               }
-                               CurrentColumn--;
-                               buffer [CurrentLine] = buffer [CurrentLine].Remove (CurrentColumn, 1);
-                       } else {
-                               int linesToRemove = selectionEnd.Y - selectionStart.Y + 1;
-                               int l = selectionStart.Y;
-
-                               if (linesToRemove > 0) {
-                                       buffer [l] = buffer [l].Remove (selectionStart.X, buffer [l].Length - selectionStart.X) +
-                                               buffer [selectionEnd.Y].Substring (selectionEnd.X, buffer [selectionEnd.Y].Length - selectionEnd.X);
-                                       l++;
-                                       for (int c = 0; c < linesToRemove-1; c++)
-                                               buffer.RemoveAt (l);
-                                       CurrentLine = selectionStart.Y;
-                                       CurrentColumn = selectionStart.X;
-                               } else
-                                       buffer [l] = buffer [l].Remove (selectionStart.X, selectionEnd.X - selectionStart.X);
-                               CurrentColumn = selectionStart.X;
-                               SelBegin = -1;
-                               SelRelease = -1;
-                       }
+                       if (!selectionIsEmpty)
+                               buffer.SetSelection (selectionStart, selectionEnd);
+                       buffer.DeleteChar ();
+                       CurrentPosition = buffer.VisualPosition;
+                       SelBegin = -1;
+                       SelRelease = -1;
                        OnTextChanged (this, null);
                }
                /// <summary>
@@ -409,15 +366,11 @@ namespace Crow.Coding
                protected void Insert(string str)
                {
                        if (!selectionIsEmpty)
-                               this.DeleteChar ();
-                       string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray();
-                       buffer [CurrentLine] = buffer [CurrentLine].Insert (CurrentColumn, strLines[0]);
-                       CurrentColumn += strLines[0].Length;
-                       for (int i = 1; i < strLines.Length; i++) {
-                               InsertLineBreak ();
-                               buffer [CurrentLine] = buffer [CurrentLine].Insert (CurrentColumn, strLines[i]);
-                               CurrentColumn += strLines[i].Length;
-                       }
+                               DeleteChar ();
+
+                       buffer.Insert (str);
+                       CurrentPosition = buffer.VisualPosition;
+
                        OnTextChanged (this, null);
                        RegisterForGraphicUpdate();
                }
@@ -426,15 +379,16 @@ namespace Crow.Coding
                /// </summary>
                protected void InsertLineBreak()
                {
-                       buffer.Insert(CurrentLine + 1, buffer[CurrentLine].Substring(CurrentColumn));
-                       buffer [CurrentLine] = buffer [CurrentLine].Substring (0, CurrentColumn);
-                       CurrentLine++;
-                       CurrentColumn = 0;
+                       buffer.InsertLineBreak ();
+                       CurrentPosition = buffer.VisualPosition;
                        OnTextChanged (this, null);
                }
                #endregion
 
                #region Drawing
+               /// <summary>
+               /// Draw unparsed buffer.
+               /// </summary>
                void draw(Context gr){
                        gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
                        gr.SetFontSize (Font.Size);
@@ -538,7 +492,7 @@ namespace Crow.Coding
                        int lPtr = 0;
 
                        for (int t = 0; t < tokens.Count; t++) {
-                               string lstr = tokens [t].Content;
+                               string lstr = tokens [t].PrintableContent;
                                if (lPtr < ScrollX) {
                                        if (lPtr - ScrollX + lstr.Length <= 0) {
                                                lPtr += lstr.Length;
@@ -808,7 +762,7 @@ namespace Crow.Coding
                        case Key.Left:
                                if (e.Shift) {
                                        if (selectionIsEmpty)
-                                               SelBegin = new Point(CurrentColumn, CurrentLine);
+                                               SelBegin = CurrentPosition;
                                        if (e.Control)
                                                GotoWordStart ();
                                        else if (!MoveLeft ())
index df9f66e7ec3bf54aa1aec1bc9b539de5b71f9671..e0cb7e0904e49fb5f152ad1c9471fbd1ebd9f677 100644 (file)
@@ -30,6 +30,10 @@ namespace Crow.Coding
                public Point Start;
                public Point End;
 
+               public string PrintableContent {
+                       get { return string.IsNullOrEmpty(Content) ? "" : Content.Replace("\t", new String(' ', Interface.TabSize)); }
+               }
+
 //             public Token (TokenType tokType, string content = ""){
 //                     Type = tokType;
 //                     Content = content;