From: Jean-Philippe Bruyère Date: Tue, 7 Nov 2017 19:10:02 +0000 (+0100) Subject: simplifications, parsing and syntax has to be reworked X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=f082b038ad796df6f52a72b1bb78b41f83c6f623;p=jp%2Fcrowedit.git simplifications, parsing and syntax has to be reworked --- diff --git a/CrowEdit.csproj b/CrowEdit.csproj index 3c197e1..08986e6 100644 --- a/CrowEdit.csproj +++ b/CrowEdit.csproj @@ -72,7 +72,6 @@ - @@ -84,6 +83,7 @@ + diff --git a/src/CSharpParser.cs b/src/CSharpParser.cs index 14e3de8..7d2e28e 100644 --- a/src/CSharpParser.cs +++ b/src/CSharpParser.cs @@ -32,7 +32,7 @@ namespace Crow.Coding { } - public override void Parse (int line) + public override void ParseCurrentLine () { throw new NotImplementedException (); } diff --git a/src/CodeBuffer.cs b/src/CodeBuffer.cs index 424aa4a..399d10a 100644 --- a/src/CodeBuffer.cs +++ b/src/CodeBuffer.cs @@ -31,23 +31,27 @@ namespace Crow.Coding /// public class CodeBuffer { + public object EditMutex = new object(); //those events are handled in SourceEditor to help keeping sync between textbuffer,parser and editor. //modified lines are marked for reparse #region Events public event EventHandler LineUpadateEvent; public event EventHandler LineRemoveEvent; public event EventHandler LineAdditionEvent; + public event EventHandler FoldingEvent; public event EventHandler BufferCleared; + public event EventHandler SelectionChanged; + public event EventHandler PositionChanged; #endregion #region CTOR public CodeBuffer () { - this.Add (""); + } #endregion string lineBreak = Interface.LineBreak; - List lines = new List(); + List lines = new List(); public int longestLineIdx = 0; public int longestLineCharCount = 0; /// @@ -57,47 +61,83 @@ namespace Crow.Coding int _currentCol = 0; public int LineCount { get { return lines.Count;}} - - /// - /// Return line with tabs replaced by spaces - /// - public string GetPrintableLine(int i){ - return string.IsNullOrEmpty(lines[i]) ? "" : lines[i].Replace("\t", new String(' ', Interface.TabSize)); + public int IndexOf (CodeLine cl) { + return lines.IndexOf (cl); } - public string this[int i] + public CodeLine this[int i] { get { return lines[i]; } set { if (lines [i] == value) return; - lines[i] = value; - LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + lock (EditMutex) { + lines [i] = value; + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } } } public void RemoveAt(int i){ - lines.RemoveAt(i); - LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i)); + lock (EditMutex) { + lines.RemoveAt (i); + LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i)); + } } public void Insert(int i, string item){ - lines.Insert (i, item); - LineAdditionEvent.Raise (this, new CodeBufferEventArgs (i)); + lock (EditMutex) { + lines.Insert (i, item); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (i)); + } } - public void Add(string item){ - lines.Add (item); - LineAdditionEvent.Raise (this, new CodeBufferEventArgs (lines.Count - 1)); + public void Add(CodeLine item){ + lock (EditMutex) { + lines.Add (item); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (lines.Count - 1)); + } } public void AddRange (string[] items){ int start = lines.Count; - lines.AddRange (items); - LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); + lock (EditMutex) { + for (int i = 0; i < items.Length; i++) + lines.Add (items [i]); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); + } + } + public void AddRange (CodeLine[] items){ + int start = lines.Count; + lock (EditMutex) { + lines.AddRange (items); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); + } } public void Clear () { - lines.Clear(); - BufferCleared.Raise (this, null); + lock (EditMutex) { + longestLineCharCount = 0; + lines.Clear (); + BufferCleared.Raise (this, null); + } + } + public void UpdateLine(int i, string newContent){ + lock (EditMutex) { + this [i].Content = newContent; + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public void AppenedLine(int i, string newContent){ + lock (EditMutex) { + this [i].Content += newContent; + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public void ToogleFolding (int line) { + if (!this [line].IsFoldable) + return; + lock (EditMutex) { + this [line].IsFolded = !this [line].IsFolded; + FoldingEvent.Raise (this, new CodeBufferEventArgs (line)); + } } - public void Load(string rawSource) { this.Clear(); @@ -107,7 +147,6 @@ namespace Crow.Coding AddRange (Regex.Split (rawSource, "\r\n|\r|\n|\\\\n")); lineBreak = detectLineBreakKind (rawSource); - FindLongestVisualLine (); } /// @@ -116,8 +155,8 @@ namespace Crow.Coding public void FindLongestVisualLine(){ longestLineCharCount = 0; for (int i = 0; i < this.LineCount; i++) { - if (this.GetPrintableLine(i).Length > longestLineCharCount) { - longestLineCharCount = this.GetPrintableLine(i).Length; + if (lines[i].PrintableLength > longestLineCharCount) { + longestLineCharCount = lines[i].PrintableLength; longestLineIdx = i; } } @@ -152,7 +191,30 @@ namespace Crow.Coding /// public string FullText{ get { - return lines.Count > 0 ? lines.Aggregate((i, j) => i + this.lineBreak + j) : ""; + if (lines.Count == 0) + return ""; + string tmp = ""; + for (int i = 0; i < lines.Count -1; i++) + tmp += lines [i].Content + this.lineBreak; + tmp += lines [lines.Count - 1].Content; + return tmp; + } + } + + /// + /// unfolded and not in folds line count + /// + public int UnfoldedLines { + get { + int i = 0, vl = 0; + while (i < LineCount) { + if (this [i].IsFolded) + i = GetEndNodeIndex (i); + i++; + vl++; + } + //Debug.WriteLine ("unfolded lines: " + vl); + return vl; } } @@ -171,56 +233,76 @@ namespace Crow.Coding } return new Point (buffCol, visualPos.Y); } + + public int GetEndNodeIndex (int line) { + return IndexOf (this [line].SyntacticNode.EndLine); + } /// /// Gets visual position computed from actual buffer position /// - public Point TabulatedPosition { - get { return new Point (TabulatedColumn, _currentLine); } +// public Point TabulatedPosition { +// get { return new Point (TabulatedColumn, _currentLine); } +// } + /// + /// set buffer current position from visual position + /// +// public void SetBufferPos(Point tabulatedPosition) { +// CurrentPosition = getBuffPos(tabulatedPosition); +// } + + #region Editing and moving cursor + Point selStartPos = -1; //selection start (row,column) + Point selEndPos = -1; //selection end (row,column) + + public bool SelectionInProgress { get { return selStartPos >= 0; }} + public void SetSelStartPos () { + selStartPos = selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); } - public int TabulatedColumn { - get { return this [_currentLine].Substring (0, _currentCol).Replace ("\t", new String (' ', Interface.TabSize)).Length; } + public void SetSelEndPos () { + selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); } /// - /// set buffer current position from visual position + /// Set selection in buffer to -1, empty selection /// - public void SetBufferPos(Point tabulatedPosition) { - CurrentPosition = getBuffPos(tabulatedPosition); + public void ResetSelection () { + selStartPos = selEndPos = -1; + SelectionChanged.Raise (this, null); } - #region Editing and moving cursor public string SelectedText { get { - if (selectionIsEmpty) + if (SelectionIsEmpty) return ""; - if (selectionStart.Y == selectionEnd.Y) - return this [selectionStart.Y].Substring (selectionStart.X, selectionEnd.X - selectionStart.X); + Point selStart = SelectionStart; + Point selEnd = SelectionEnd; + if (selStart.Y == selEnd.Y) + return this [selStart.Y].Content.Substring (selStart.X, selEnd.X - selStart.X); string tmp = ""; - tmp = this [selectionStart.Y].Substring (selectionStart.X); - for (int l = selectionStart.Y + 1; l < selectionEnd.Y; l++) { - tmp += Interface.LineBreak + this [l]; + tmp = this [selStart.Y].Content.Substring (selStart.X); + for (int l = selStart.Y + 1; l < selEnd.Y; l++) { + tmp += Interface.LineBreak + this [l].Content; } - tmp += Interface.LineBreak + this [selectionEnd.Y].Substring (0, selectionEnd.X); + tmp += Interface.LineBreak + this [selEnd.Y].Content.Substring (0, selEnd.X); return tmp; } } - Point selectionStart = -1; - Point selectionEnd = -1; /// - /// Set selection in buffer coords from tabulated coords + /// ordered selection start and end positions in char units /// - public void SetSelection (Point tabulatedStart, Point tabulatedEnd) { - selectionStart = getBuffPos (tabulatedStart); - selectionEnd = getBuffPos (tabulatedEnd); + public Point SelectionStart { + get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos : + selStartPos.Y > selEndPos.Y ? selEndPos : + selStartPos.X < selEndPos.X ? selStartPos : selEndPos; } } - /// - /// Set selection in buffer to -1, empty selection - /// - public void ResetSelection () { - selectionStart = selectionEnd = -1; - } - bool selectionIsEmpty { - get { return selectionStart == selectionEnd; } + public Point SelectionEnd { + get { return selEndPos < 0 || selStartPos.Y > selEndPos.Y ? selStartPos : + selStartPos.Y < selEndPos.Y ? selEndPos : + selStartPos.X > selEndPos.X ? selStartPos : selEndPos; } } + public bool SelectionIsEmpty + { get { return selEndPos == selStartPos; } } /// /// Current column in buffer coordinate, tabulation = 1 char /// @@ -235,6 +317,8 @@ namespace Crow.Coding _currentCol = lines [_currentLine].Length; else _currentCol = value; + + PositionChanged.Raise (this, null); } } /// @@ -251,57 +335,31 @@ namespace Crow.Coding _currentLine = 0; else _currentLine = value; - //force recheck of currentCol for bounding - int cc = _currentCol; - _currentCol = 0; - CurrentColumn = cc; + + if (_currentCol > lines [_currentLine].Length) + _currentCol = lines [_currentLine].Length; + Debug.WriteLine ("buff cur line: " + _currentLine); + PositionChanged.Raise (this, null); } } + public CodeLine CurrentCodeLine { + get { return this [_currentLine]; } + } /// /// Current position in buffer coordinate, tabulation = 1 char /// public Point CurrentPosition { get { return new Point(CurrentColumn, CurrentLine); } - set { - _currentCol = value.X; - _currentLine = value.Y; - } +// set { +// _currentCol = value.X; +// _currentLine = value.Y; +// } } /// /// get char at current position in buffer /// protected Char CurrentChar { get { return lines [CurrentLine] [CurrentColumn]; } } - /// - /// Moves cursor one char to the left, move up if cursor reaches start of line - /// - /// true if move succeed - public bool MoveLeft(){ - int tmp = _currentCol - 1; - if (tmp < 0) { - if (_currentLine == 0) - return false; - _currentCol = int.MaxValue; - CurrentLine--; - } else - CurrentColumn = tmp; - return true; - } - /// - /// Moves cursor one char to the right, move down if cursor reaches end of line - /// - /// true if move succeed - public bool MoveRight(){ - int tmp = _currentCol + 1; - if (tmp > this [_currentLine].Length){ - if (CurrentLine == this.LineCount - 1) - return false; - CurrentLine++; - CurrentColumn = 0; - } else - CurrentColumn = tmp; - return true; - } public void GotoWordStart(){ if (this[CurrentLine].Length == 0) return; @@ -327,34 +385,36 @@ namespace Crow.Coding } public void DeleteChar() { - if (selectionIsEmpty) { - if (CurrentColumn == 0) { - if (CurrentLine == 0 && this.LineCount == 1) + lock (EditMutex) { + if (SelectionIsEmpty) { + if (CurrentColumn == 0) { + if (CurrentLine == 0) + return; + CurrentLine--; + CurrentColumn = this [CurrentLine].Length; + AppenedLine (CurrentLine, this [CurrentLine + 1].Content); + RemoveAt (CurrentLine + 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; + } + CurrentColumn--; + UpdateLine (CurrentLine, this [CurrentLine].Content.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 (); + if (linesToRemove > 0) { + UpdateLine (l, this [l].Content.Remove (SelectionStart.X, this [l].Length - SelectionStart.X) + + this [SelectionEnd.Y].Content.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 + UpdateLine (l, this [l].Content.Remove (SelectionStart.X, SelectionEnd.X - SelectionStart.X)); + CurrentColumn = SelectionStart.X; + ResetSelection (); + } } } /// @@ -363,14 +423,14 @@ namespace Crow.Coding /// String. public void Insert(string str) { - if (!selectionIsEmpty) + if (!SelectionIsEmpty) this.DeleteChar (); string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray(); - this [CurrentLine] = this [CurrentLine].Insert (CurrentColumn, strLines[0]); + UpdateLine (CurrentLine, this [CurrentLine].Content.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]); + UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[i])); CurrentColumn += strLines[i].Length; } } @@ -380,13 +440,13 @@ namespace Crow.Coding public void InsertLineBreak() { if (CurrentColumn > 0) { - Insert (CurrentLine + 1, this [CurrentLine].Substring (CurrentColumn)); - this [CurrentLine] = this [CurrentLine].Substring (0, CurrentColumn); + Insert (CurrentLine + 1, this [CurrentLine].Content.Substring (CurrentColumn)); + UpdateLine (CurrentLine, this [CurrentLine].Content.Substring (0, CurrentColumn)); } else Insert(CurrentLine, ""); - CurrentLine++; CurrentColumn = 0; + CurrentLine++; } #endregion } diff --git a/src/CodeLine.cs b/src/CodeLine.cs new file mode 100644 index 0000000..622584c --- /dev/null +++ b/src/CodeLine.cs @@ -0,0 +1,76 @@ +using System; +using System.Text; +using System.Collections.Generic; + +namespace Crow.Coding +{ + public class CodeLine + { + public string Content; + public List Tokens; + public int EndingState = 0; + public Node SyntacticNode; + public ParsingException exception; + + public CodeLine (string _content){ + Content = _content; + Tokens = null; + exception = null; + } + + public char this[int i] + { + get { return Content[i]; } + set { + if (Content [i] == value) + return; + StringBuilder sb = new StringBuilder(Content); + sb[i] = value; + Content = sb.ToString(); + Tokens = null; + //LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public bool IsFoldable { get { return SyntacticNode != null; } } + public bool IsFolded = false; + public bool IsParsed { + get { return Tokens != null; } + } + public string PrintableContent { + get { + return string.IsNullOrEmpty (Content) ? "" : Content.Replace ("\t", new String (' ', Interface.TabSize)); + } + } + public int PrintableLength { + get { + return PrintableContent.Length; + } + } + public int Length { + get { + return string.IsNullOrEmpty (Content) ? 0 : Content.Length; + } + } + + public void SetLineInError (ParsingException ex) { + Tokens = null; + exception = ex; + } + +// public static implicit operator string(CodeLine sl) { +// return sl == null ? "" : sl.Content; +// } + public static implicit operator CodeLine(string s) { + return new CodeLine(s); + } + public static bool operator ==(string s1, CodeLine s2) + { + return string.Equals (s1, s2.Content); + } + public static bool operator !=(string s1, CodeLine s2) + { + return !string.Equals (s1, s2.Content); + } + } +} + diff --git a/src/Node.cs b/src/Node.cs index 94eceb6..24818e0 100644 --- a/src/Node.cs +++ b/src/Node.cs @@ -8,8 +8,8 @@ namespace Crow.Coding public Node Parent; public string Name; public string Type; - public int StartLine; - public int EndLine; + public CodeLine StartLine; + public CodeLine EndLine; public Dictionary Attributes = new Dictionary (); public List Children = new List(); diff --git a/src/Parser.cs b/src/Parser.cs index dcb2c62..79dc086 100644 --- a/src/Parser.cs +++ b/src/Parser.cs @@ -20,7 +20,9 @@ namespace Crow.Coding WhiteSpace, NewLine, LineComment, + BlockCommentStart, BlockComment, + BlockCommentEnd, Type, Identifier, Indexer, @@ -46,10 +48,6 @@ namespace Crow.Coding //buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; buffer.LineRemoveEvent += Buffer_LineRemoveEvent; buffer.BufferCleared += Buffer_BufferCleared; - - Tokens = new List (); - if (buffer.LineCount > 0) - eof = false; } #endregion @@ -57,7 +55,7 @@ namespace Crow.Coding #region Buffer events handlers void Buffer_BufferCleared (object sender, EventArgs e) { - Tokens.Clear (); + } void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) { @@ -65,8 +63,6 @@ namespace Crow.Coding } void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) { - for (int i = 0; i < e.LineCount; i++) - Tokens.RemoveAt (e.LineStart + i); reparseSource (); } void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) @@ -117,8 +113,8 @@ namespace Crow.Coding // } } public void reparseSource () { - for (int i = 0; i < Tokens.Count; i++) { - if (Tokens[i].Dirty) + for (int i = 0; i < buffer.LineCount; i++) { + if (!buffer[i].IsParsed) tryParseBufferLine (i); } try { @@ -130,8 +126,13 @@ namespace Crow.Coding } } public void tryParseBufferLine(int lPtr) { + buffer [lPtr].exception = null; + currentLine = lPtr; + currentColumn = 0; + eol = false; + try { - Parse (lPtr); + ParseCurrentLine (); } catch (Exception ex) { Debug.WriteLine (ex.ToString ()); if (ex is ParsingException) @@ -140,54 +141,33 @@ namespace Crow.Coding } - CodeBuffer buffer; + protected CodeBuffer buffer; internal int currentLine = 0; internal int currentColumn = 0; protected Token currentTok; - protected bool eof = true; - - public List Tokens; - protected TokenList TokensLine; + protected bool eol = true; public Node RootNode; - public Point CurrentPosition { + protected Point CurrentPosition { get { return new Point (currentLine, currentColumn); } set { currentLine = value.Y; currentColumn = value.X; } } - public int LineCount { - get { return Tokens.Count; } - } - /// - /// unfolded and not in folds line count - /// - public int VisibleLines { - get { - int i = 0, vl = 0; - while (i= buffer.LineCount) + ex.Line = buffer.LineCount - 1; + if (buffer [ex.Line].IsFolded) + buffer.ToogleFolding (ex.Line); + buffer [ex.Line].SetLineInError (ex); } #region low level parsing @@ -212,7 +192,7 @@ namespace Crow.Coding /// protected void saveAndResetCurrentTok() { currentTok.End = CurrentPosition; - TokensLine.Add (currentTok); + buffer[currentLine].Tokens.Add (currentTok); currentTok = default(Token); } /// @@ -237,8 +217,8 @@ namespace Crow.Coding /// Throw error if eof is true /// protected virtual char Peek() { - if (eof) - throw new ParsingException (this, "Unexpected End of File"); + if (eol) + throw new ParsingException (this, "Unexpected End of line"); return currentColumn < buffer [currentLine].Length ? buffer [currentLine] [currentColumn] : '\n'; } @@ -250,12 +230,12 @@ namespace Crow.Coding /// /// Length. protected virtual string Peek(int length) { - if (eof) - throw new ParsingException (this, "Unexpected End of File"); + if (eol) + throw new ParsingException (this, "Unexpected End of Line"); int lg = Math.Min(length, Math.Max (buffer [currentLine].Length - currentColumn, buffer [currentLine].Length - currentColumn - length)); if (lg == 0) return ""; - return buffer [currentLine].Substring (currentColumn, lg); + return buffer [currentLine].Content.Substring (currentColumn, lg); } /// /// read one char from buffer at current position, if '\n' is read, current line is incremented @@ -263,14 +243,9 @@ namespace Crow.Coding /// protected virtual char Read() { char c = Peek (); - //TODO: the parsing is done line by line, we should be able to remove the next line handling from read - if (c == '\n') { - currentLine++; - if (currentLine >= buffer.LineCount) - eof = true; - currentColumn = 0; - } else - currentColumn++; + if (c == '\n') + eol = true; + currentColumn++; return c; } /// @@ -279,11 +254,8 @@ namespace Crow.Coding /// string read protected virtual string ReadLine () { string tmp = ""; - while (!eof) { - if (Peek () == '\n') - return tmp; + while (!eol) tmp += Read (); - } return tmp; } /// @@ -294,7 +266,7 @@ namespace Crow.Coding protected virtual string ReadLineUntil (string endExp){ string tmp = ""; - while (!eof) { + while (!eol) { if (buffer [currentLine].Length - currentColumn - endExp.Length < 0) { tmp += ReadLine(); break; @@ -311,7 +283,7 @@ namespace Crow.Coding protected void SkipWhiteSpaces () { if (currentTok.Type != TokenType.Unknown) throw new ParsingException (this, "current token should be reset to unknown (0) before skiping white spaces"); - while (!eof) { + while (!eol) { if (!char.IsWhiteSpace (Peek ())||Peek()=='\n') break; readToCurrTok (currentTok.Type == TokenType.Unknown); diff --git a/src/SourceEditor.cs b/src/SourceEditor.cs index 2c53ba7..57c5757 100644 --- a/src/SourceEditor.cs +++ b/src/SourceEditor.cs @@ -45,7 +45,7 @@ namespace Crow.Coding public class SourceEditor : ScrollingObject { #region CTOR - public SourceEditor ():base() + public SourceEditor (): base() { formatting.Add ((int)XMLParser.TokenType.AttributeName, new TextFormatting (Color.UnitedNationsBlue, Color.Transparent)); formatting.Add ((int)XMLParser.TokenType.ElementName, new TextFormatting (Color.DarkBlue, Color.Transparent)); @@ -67,10 +67,14 @@ namespace Crow.Coding buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; buffer.LineRemoveEvent += Buffer_LineRemoveEvent; buffer.BufferCleared += Buffer_BufferCleared; + buffer.SelectionChanged += Buffer_SelectionChanged; + buffer.PositionChanged += Buffer_PositionChanged; + buffer.FoldingEvent += Buffer_FoldingEvent; + buffer.Add (new CodeLine("")); } #endregion - const int leftMarginGap = 5;//gap between items in margin and text + const int leftMarginGap = 3;//gap between items in margin and text const int foldSize = 9;//folding rectangles size #region private and protected fields @@ -83,10 +87,9 @@ namespace Crow.Coding Parser parser; Color selBackground; Color selForeground; - int _currentCol; //0 based cursor position in string - int _currentLine; - Point _selBegin = -1; //selection start (row,column) - Point _selRelease = -1; //selection end (row,column) +// int _currentCol; //0 based cursor position in string +// int _currentLine; + Dictionary formatting = new Dictionary(); Dictionary parsing = new Dictionary(); @@ -132,52 +135,46 @@ namespace Crow.Coding if (parser == null || !foldingEnabled) MaxScrollY = Math.Max (0, buffer.LineCount - visibleLines); else - MaxScrollY = Math.Max (0, parser.VisibleLines - visibleLines); + MaxScrollY = Math.Max (0, buffer.UnfoldedLines - visibleLines); } int firstPrintedLine = -1; /// /// list of lines visible in the Editor depending on scrolling and folding /// - List PrintedLines; + List PrintedLines; void updatePrintedLines () { - if (parser == null) - return; - PrintedLines = new List (); - int curL = 0; - int i = 0; - - while (curL < parser.LineCount && i < ScrollY) { - if (parser.Tokens [curL].folded && parser.Tokens [curL].SyntacticNode != null) - curL = parser.Tokens [curL].SyntacticNode.EndLine; - curL++; - i++; - } + lock (buffer.EditMutex) { + PrintedLines = new List (); + int curL = 0; + int i = 0; + + while (curL < buffer.LineCount && i < ScrollY) { + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); + curL++; + i++; + } - firstPrintedLine = curL; - i = 0; - while (i < visibleLines && curL < parser.LineCount) { - PrintedLines.Add (parser.Tokens [curL]); + firstPrintedLine = curL; + i = 0; + while (i < visibleLines && curL < buffer.LineCount) { + PrintedLines.Add (buffer [curL]); - if (parser.Tokens [curL].folded && parser.Tokens [curL].SyntacticNode != null) - curL = parser.Tokens [curL].SyntacticNode.EndLine; + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); - curL++; - i++; - } + curL++; + i++; + } + } RegisterForGraphicUpdate (); } void toogleFolding (int line) { if (parser == null || !foldingEnabled) return; - if (parser.Tokens [line].SyntacticNode == null) - return; - parser.Tokens [line].folded = !parser.Tokens [line].folded; - updatePrintedLines (); - updateMaxScrollY (); - - RegisterForGraphicUpdate (); + buffer.ToogleFolding (line); } #region Buffer events handlers @@ -187,13 +184,14 @@ namespace Crow.Coding buffer.longestLineIdx = 0; measureLeftMargin (); MaxScrollX = MaxScrollY = 0; + PrintedLines = null; RegisterForGraphicUpdate (); } void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) { for (int i = 0; i < e.LineCount; i++) { int lptr = e.LineStart + i; - int charCount = buffer.GetPrintableLine (lptr).Length; + int charCount = buffer[lptr].PrintableLength; if (charCount > buffer.longestLineCharCount) { buffer.longestLineIdx = lptr; buffer.longestLineCharCount = charCount; @@ -201,7 +199,6 @@ namespace Crow.Coding buffer.longestLineIdx++; if (parser == null) continue; - parser.Tokens.Insert (lptr, new TokenList ()); parser.tryParseBufferLine (e.LineStart + i); } measureLeftMargin (); @@ -209,13 +206,10 @@ namespace Crow.Coding if (parser != null) parser.reparseSource (); - updatePrintedLines (); - updateOnScreenPosFromBuffPos (); - + updateMaxScrollY (); RegisterForGraphicUpdate (); } - void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) { bool trigFindLongestLine = false; @@ -226,20 +220,22 @@ namespace Crow.Coding } if (trigFindLongestLine) findLongestLineAndUpdateMaxScrollX (); + measureLeftMargin (); updatePrintedLines (); + updateMaxScrollY (); RegisterForGraphicUpdate (); } - void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) { bool trigFindLongestLine = false; for (int i = 0; i < e.LineCount; i++) { + int lptr = e.LineStart + i; if (lptr == buffer.longestLineIdx) trigFindLongestLine = true; - else if (buffer.GetPrintableLine (lptr).Length > buffer.longestLineCharCount) { - buffer.longestLineCharCount = buffer.GetPrintableLine (lptr).Length; + else if (buffer[lptr].PrintableLength > buffer.longestLineCharCount) { + buffer.longestLineCharCount = buffer[lptr].PrintableLength; buffer.longestLineIdx = lptr; } } @@ -247,6 +243,21 @@ namespace Crow.Coding findLongestLineAndUpdateMaxScrollX (); RegisterForGraphicUpdate (); } + void Buffer_PositionChanged (object sender, EventArgs e) + { + updateOnScreenCurLineFromBuffCurLine (); + } + + void Buffer_SelectionChanged (object sender, EventArgs e) + { + RegisterForGraphicUpdate (); + } + void Buffer_FoldingEvent (object sender, CodeBufferEventArgs e) + { + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } #endregion Parser getParserFromExt (string extension) { @@ -333,229 +344,101 @@ namespace Crow.Coding RegisterForRedraw (); } } - [XmlAttributeAttribute][DefaultValue(0)] - public int CurrentColumn{ - get { return _currentCol; } - set { - if (value == _currentCol) - return; - if (value < 0) - _currentCol = 0; - 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) - ScrollX = _currentCol - visibleColumns + 1; - - NotifyValueChanged ("CurrentColumn", _currentCol); - } - } - [XmlAttributeAttribute][DefaultValue(0)] - public int CurrentLine{ - get { return _currentLine; } - set { - if (value == _currentLine) - return; - if (value >= buffer.LineCount) - _currentLine = buffer.LineCount-1; - else if (value < 0) - _currentLine = 0; - else - _currentLine = value; - - if (_currentCol > buffer.GetPrintableLine(_currentLine).Length) - CurrentColumn = buffer.GetPrintableLine(_currentLine).Length;//buffer.setBufferPos is called inside - else - buffer.SetBufferPos (CurrentPosition); - -// if (_currentLine < ScrollY) -// ScrollY = _currentLine; -// else if (_currentLine >= ScrollY + visibleLines) -// ScrollY = _currentLine - visibleLines + 1; - - NotifyValueChanged ("CurrentLine", _currentLine); - } - } - /// - /// Current position is in the printed coord system, tabulation chars are replaced with 4 spaces, - /// while in the buffer, the position holds tabulations as single chars - /// - /// The current position. - [XmlIgnore]public Point CurrentPosition { - get { return new Point(CurrentColumn, CurrentLine); } - set { - _currentCol = value.X; - _currentLine = value.Y; - - if (_currentCol < ScrollX) - ScrollX = _currentCol; - else if (_currentCol >= ScrollX + visibleColumns) - ScrollX = _currentCol - visibleColumns + 1; +// [XmlAttributeAttribute][DefaultValue(0)] +// public int CurrentColumn{ +// get { return _currentCol; } +//// set { +//// if (value == _currentCol) +//// return; +//// if (value < 0) +//// _currentCol = 0; +//// else if (value > buffer[_currentLine].PrintableLength) +//// _currentCol = buffer[_currentLine].PrintableLength; +//// else +//// _currentCol = value; +//// +//// buffer.SetBufferPos (CurrentPosition); +//// +//// if (_currentCol < ScrollX) +//// ScrollX = _currentCol; +//// else if (_currentCol >= ScrollX + visibleColumns) +//// ScrollX = _currentCol - visibleColumns + 1; +//// +//// NotifyValueChanged ("CurrentColumn", _currentCol); +//// } +// } +// [XmlAttributeAttribute][DefaultValue(0)] +// public int CurrentLine{ +// get { return _currentLine; } +//// set { +//// if (value == _currentLine) +//// return; +//// if (value >= buffer.LineCount) +//// _currentLine = buffer.LineCount-1; +//// else if (value < 0) +//// _currentLine = 0; +//// else +//// _currentLine = value; +//// +//// if (_currentCol > buffer[_currentLine].PrintableLength) +//// CurrentColumn = buffer[_currentLine].PrintableLength;//buffer.setBufferPos is called inside +//// else +//// buffer.SetBufferPos (CurrentPosition); +//// +////// if (_currentLine < ScrollY) +////// ScrollY = _currentLine; +////// else if (_currentLine >= ScrollY + visibleLines) +////// ScrollY = _currentLine - visibleLines + 1; +//// +//// NotifyValueChanged ("CurrentLine", _currentLine); +//// } +// } +// /// +// /// Current position is in the printed coord system, tabulation chars are replaced with 4 spaces, +// /// while in the buffer, the position holds tabulations as single chars +// /// +// /// The current position. +// [XmlIgnore]public Point CurrentPosition { +// get { return new Point(CurrentColumn, CurrentLine); } +// set { +// _currentCol = value.X; +// _currentLine = value.Y; // -// if (_currentLine < ScrollY) -// ScrollY = _currentLine; -// else if (_currentLine >= ScrollY + visibleLines) -// ScrollY = _currentLine - visibleLines + 1; +// if (_currentCol < ScrollX) +// ScrollX = _currentCol; +// else if (_currentCol >= ScrollX + visibleColumns) +// ScrollX = _currentCol - visibleColumns + 1; +//// +//// if (_currentLine < ScrollY) +//// ScrollY = _currentLine; +//// else if (_currentLine >= ScrollY + visibleLines) +//// ScrollY = _currentLine - visibleLines + 1; +// +// NotifyValueChanged ("CurrentColumn", _currentCol); +// NotifyValueChanged ("CurrentLine", _currentLine); +// } +// } + +// [XmlIgnore]public string SelectedText +// { +// get { +// if (!selectionIsEmpty) +// buffer.SetSelection (selectionStart, selectionEnd); +// return buffer.SelectedText; +// } +// } - NotifyValueChanged ("CurrentColumn", _currentCol); - NotifyValueChanged ("CurrentLine", _currentLine); - } - } - //TODO:using HasFocus for drawing selection cause SelBegin and Release binding not to work - /// - /// Selection begin position in char units (line, column) - /// - [XmlAttributeAttribute][DefaultValue("-1")] - public Point SelBegin { - get { return _selBegin; } - set { - if (value == _selBegin) - return; - _selBegin = value; - System.Diagnostics.Debug.WriteLine ("SelBegin=" + _selBegin); - NotifyValueChanged ("SelBegin", _selBegin); - //NotifyValueChanged ("SelectedText", SelectedText); - } - } - /// - /// Selection release position in char units (line, column) - /// - [XmlAttributeAttribute][DefaultValue("-1")] - public Point SelRelease { - get { - return _selRelease; - } - set { - if (value == _selRelease) - return; - _selRelease = value; - System.Diagnostics.Debug.WriteLine ("SelRelease=" + _selRelease); - NotifyValueChanged ("SelRelease", _selRelease); - //NotifyValueChanged ("SelectedText", SelectedText); - } - } - /// - /// ordered selection start and end positions in char units - /// - [XmlIgnore]protected Point selectionStart - { - get { - return SelRelease < 0 || SelBegin.Y < SelRelease.Y ? SelBegin : - SelBegin.Y > SelRelease.Y ? SelRelease : - SelBegin.X < SelRelease.X ? SelBegin : SelRelease; - } - } - [XmlIgnore]public Point selectionEnd - { - get { - return SelRelease < 0 || SelBegin.Y > SelRelease.Y ? SelBegin : - SelBegin.Y < SelRelease.Y ? SelRelease : - SelBegin.X > SelRelease.X ? SelBegin : SelRelease; - } - } - [XmlIgnore]public string SelectedText - { - get { - if (!selectionIsEmpty) - buffer.SetSelection (selectionStart, selectionEnd); - return buffer.SelectedText; - } - } - [XmlIgnore]public bool selectionIsEmpty - { get { return SelRelease == SelBegin; } } #endregion - #region Editing and moving cursor - /// - /// Moves cursor one char to the left. - /// - /// true if move succeed - public void MoveLeft(){ - if (CurrentPosition == 0) - return; - if (_currentCol == 0) { - PrintedCurrentLine--; - CurrentColumn = int.MaxValue; - } else { - //do move in the buffer so that tabulations are treated as single char - buffer.CurrentColumn --; - CurrentPosition = buffer.TabulatedPosition; - } - } - /// - /// Moves cursor one char to the right. - /// - /// true if move succeed - public void MoveRight(){ - if (_currentCol == buffer.GetPrintableLine(CurrentLine).Length && _currentLine < buffer.LineCount - 1) { - PrintedCurrentLine++; - CurrentColumn = 0; - } else { - //do move in the buffer so that tabulations are treated as single char - buffer.CurrentColumn ++; - CurrentPosition = buffer.TabulatedPosition; - } - } - public void MoveUp (){ - PrintedCurrentLine--; - } - public void MoveDown (){ - PrintedCurrentLine++; - } - public void GotoWordStart(){ - buffer.GotoWordStart(); - updateOnScreenPosFromBuffPos (); - } - public void GotoWordEnd(){ - buffer.GotoWordEnd(); - updateOnScreenPosFromBuffPos (); - } - - public void DeleteChar() - { - if (!selectionIsEmpty) - buffer.SetSelection (selectionStart, selectionEnd); - buffer.DeleteChar (); - updateOnScreenPosFromBuffPos (); - SelBegin = -1; - SelRelease = -1; - } - /// - /// Insert new string at caret position, should be sure no line break is inside. - /// - /// String. - protected void Insert(string str) - { - if (!selectionIsEmpty) - DeleteChar (); - - buffer.Insert (str); - CurrentPosition = buffer.TabulatedPosition; - - RegisterForGraphicUpdate(); - } - #endregion - void updateOnScreenPosFromBuffPos(){ - if (parser.Tokens.Count == 0 || PrintedLines.Count == 0) - return; - if (!PrintedLines.Contains (parser.Tokens [buffer.CurrentLine])) - return; - printedCurrentLine = PrintedLines.IndexOf (parser.Tokens [buffer.CurrentLine]); - setCurrentLineFromBuffer (); - CurrentColumn = buffer.TabulatedColumn; + void updateOnScreenCurLineFromBuffCurLine(){ + printedCurrentLine = PrintedLines.IndexOf (buffer.CurrentCodeLine); } - void setCurrentLineFromBuffer () { - _currentLine = buffer.CurrentLine; - NotifyValueChanged ("CurrentLine", _currentLine); - } +// void setCurrentLineFromBuffer () { +// _currentLine = buffer.CurrentLine; +// NotifyValueChanged ("CurrentLine", _currentLine); +// } public override int ScrollY { get { @@ -575,6 +458,9 @@ namespace Crow.Coding /// int printedCurrentLine = 0; + /// + /// Current editor line, when set, update buffer.CurrentLine + /// int PrintedCurrentLine { get { return printedCurrentLine;} set { @@ -588,158 +474,86 @@ namespace Crow.Coding printedCurrentLine = visibleLines - 1; }else printedCurrentLine = value; - + Debug.WriteLine ("printed current line:" + printedCurrentLine.ToString ()); //update position in buffer - CurrentLine = parser.Tokens.IndexOf (PrintedLines[printedCurrentLine]); + buffer.CurrentLine = buffer.IndexOf (PrintedLines[printedCurrentLine]); } } - - - #region Drawing + int getTabulatedColumn (int col, int line) { + return buffer [line].Content.Substring (0, col).Replace ("\t", new String (' ', Interface.TabSize)).Length; + } + int getTabulatedColumn (Point pos) { + return getTabulatedColumn (pos.X,pos.Y); + } /// - /// Draw unparsed buffer. + /// Moves cursor one char to the left, move up if cursor reaches start of line /// - void draw(Context gr){ - gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); - gr.SetFontSize (Font.Size); - gr.FontOptions = Interface.FontRenderingOptions; - gr.Antialias = Interface.Antialias; - - Rectangle cb = ClientRectangle; - - bool selectionInProgress = false; - - Foreground.SetAsSource (gr); - - #region draw text cursor - if (SelBegin != SelRelease) - selectionInProgress = true; - else if (HasFocus){ - gr.LineWidth = 1.0; - double cursorX = cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance + leftMargin; - gr.MoveTo (0.5 + cursorX, cb.Y + (CurrentLine - ScrollY) * fe.Height); - gr.LineTo (0.5 + cursorX, cb.Y + (CurrentLine + 1 - ScrollY) * fe.Height); - gr.Stroke(); - } - #endregion - - for (int i = 0; i < visibleLines; i++) { - int curL = i + ScrollY; - if (curL >= buffer.LineCount) - break; - string lstr = buffer.GetPrintableLine(curL); - if (ScrollX < lstr.Length) - lstr = lstr.Substring (ScrollX); - else - lstr = ""; - - gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i); - gr.ShowText (lstr); - gr.Fill (); - - if (selectionInProgress && curL >= selectionStart.Y && curL <= selectionEnd.Y) { - - double rLineX = cb.X, - rLineY = cb.Y + i * fe.Height, - rLineW = lstr.Length * fe.MaxXAdvance; - - System.Diagnostics.Debug.WriteLine ("sel start: " + selectionStart + " sel end: " + selectionEnd); - if (curL == selectionStart.Y) { - rLineX += (selectionStart.X - ScrollX) * fe.MaxXAdvance; - rLineW -= selectionStart.X * fe.MaxXAdvance; - } - if (curL == selectionEnd.Y) - rLineW -= (lstr.Length - selectionEnd.X) * fe.MaxXAdvance; - - gr.Save (); - gr.Operator = Operator.Source; - gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); - gr.SetSourceColor (SelectionBackground); - gr.FillPreserve (); - gr.Clip (); - gr.Operator = Operator.Over; - gr.SetSourceColor (SelectionForeground); - gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i); - gr.ShowText (lstr); - gr.Fill (); - gr.Restore (); - } - } + /// true if move succeed + public bool MoveLeft(){ + if (buffer.CurrentColumn == 0) { + if (printedCurrentLine == 0) + return false; + PrintedCurrentLine--; + buffer.CurrentColumn = int.MaxValue; + } else + buffer.CurrentColumn--; + return true; } - void drawParsed(Context gr){ - if (PrintedLines == null) - return; - - gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); - gr.SetFontSize (Font.Size); - gr.FontOptions = Interface.FontRenderingOptions; - gr.Antialias = Interface.Antialias; - - Rectangle cb = ClientRectangle; - gr.Save (); - CairoHelpers.CairoRectangle (gr, cb, CornerRadius); - gr.Clip (); - - bool selectionInProgress = false; - - Foreground.SetAsSource (gr); - - #region draw text cursor - if (SelBegin != SelRelease) - selectionInProgress = true; - else if (HasFocus){ - gr.LineWidth = 1.0; - double cursorX = + leftMargin + cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance; - gr.MoveTo (0.5 + cursorX, cb.Y + printedCurrentLine * fe.Height); - gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); - gr.Stroke(); - } - #endregion - - for (int i = 0; i < PrintedLines.Count; i++) - drawTokenLine (gr, i, selectionInProgress, cb); - - gr.Restore (); + /// + /// Moves cursor one char to the right, move down if cursor reaches end of line + /// + /// true if move succeed + public bool MoveRight(){ + if (buffer.CurrentColumn >= buffer.CurrentCodeLine.Length) { + if (PrintedCurrentLine == buffer.UnfoldedLines - 1) + return false; + buffer.CurrentColumn = 0; + PrintedCurrentLine++; + } else + buffer.CurrentColumn++; + return true; } - void drawTokenLine(Context gr, int i, bool selectionInProgress, Rectangle cb) { - TokenList tokens = PrintedLines[i]; - int lineIndex = parser.Tokens.IndexOf(tokens); + #region Drawing + void drawLine(Context gr, Rectangle cb, int i) { + CodeLine cl = PrintedLines[i]; + int lineIndex = buffer.IndexOf(cl); - int lPtr = 0; - double y = cb.Y + fe.Height * i; + double y = cb.Y + fe.Height * i, x = cb.X; //Draw line numbering Color mgFg = Color.Gray; Color mgBg = Color.White; if (PrintLineNumbers){ - Rectangle mgR = new Rectangle (cb.X, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling(fe.Height)); - if (tokens.exception != null) { + Rectangle mgR = new Rectangle ((int)x, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling(fe.Height)); + if (cl.exception != null) { mgBg = Color.Red; - if (CurrentLine == lineIndex) + if (buffer.CurrentLine == lineIndex) mgFg = Color.White; else mgFg = Color.LightGray; - }else if (CurrentLine == lineIndex) { + }else if (buffer.CurrentLine == lineIndex) { mgFg = Color.Black; } - string strLN = lineIndex.ToString (); + string strLN = (lineIndex+1).ToString (); gr.SetSourceColor (mgBg); gr.Rectangle (mgR); gr.Fill(); gr.SetSourceColor (mgFg); - gr.MoveTo (cb.X + (int)(gr.TextExtents (parser.Tokens.Count.ToString()).Width - gr.TextExtents (strLN).Width), y + fe.Ascent); + gr.MoveTo (cb.X + (int)(gr.TextExtents (buffer.LineCount.ToString()).Width - gr.TextExtents (strLN).Width), y + fe.Ascent); gr.ShowText (strLN); gr.Fill (); } + + //draw folding if (foldingEnabled){ - if (tokens.SyntacticNode != null) { - if (tokens.SyntacticNode.StartLine < tokens.SyntacticNode.EndLine) { + if (cl.IsFoldable) { + if (cl.SyntacticNode.StartLine != cl.SyntacticNode.EndLine) { gr.SetSourceColor (Color.Black); Rectangle rFld = new Rectangle (cb.X + leftMargin - leftMarginGap - foldSize, (int)(y + fe.Height / 2.0 - foldSize / 2.0), foldSize, foldSize); gr.Rectangle (rFld, 1.0); - if (tokens.folded) { + if (cl.IsFolded) { gr.MoveTo (rFld.Center.X + 0.5, rFld.Y + 2); gr.LineTo (rFld.Center.X + 0.5, rFld.Bottom - 2); } @@ -750,8 +564,93 @@ namespace Crow.Coding } } - for (int t = 0; t < tokens.Count; t++) { - string lstr = tokens [t].PrintableContent; + gr.SetSourceColor (Foreground); + x += leftMargin; + + if (cl.Tokens == null) + drawRawCodeLine (gr, x, y, i, lineIndex); + else + drawParsedCodeLine (gr, x, y, i, lineIndex); + } +// void drawParsed(Context gr){ +// if (PrintedLines == null) +// return; +// +// gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); +// gr.SetFontSize (Font.Size); +// gr.FontOptions = Interface.FontRenderingOptions; +// gr.Antialias = Interface.Antialias; +// +// Rectangle cb = ClientRectangle; +// gr.Save (); +// CairoHelpers.CairoRectangle (gr, cb, CornerRadius); +// gr.Clip (); +// +// bool selectionInProgress = false; +// +// Foreground.SetAsSource (gr); +// +// #region draw text cursor +// if (SelBegin != SelRelease) +// selectionInProgress = true; +// else if (HasFocus){ +// gr.LineWidth = 1.0; +// double cursorX = + leftMargin + cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance; +// gr.MoveTo (0.5 + cursorX, cb.Y + printedCurrentLine * fe.Height); +// gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); +// gr.Stroke(); +// } +// #endregion +// +// for (int i = 0; i < PrintedLines.Count; i++) +// drawTokenLine (gr, i, selectionInProgress, cb); +// +// gr.Restore (); +// } + void drawRawCodeLine(Context gr, double x, double y, int i, int lineIndex) { + string lstr = buffer[lineIndex].PrintableContent; + if (ScrollX < lstr.Length) + lstr = lstr.Substring (ScrollX); + else + lstr = ""; + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (!buffer.SelectionIsEmpty && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y) { + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + + //System.Diagnostics.Debug.WriteLine ("sel start: " + buffer.SelectionStart + " sel end: " + buffer.SelectionEnd); + if (lineIndex == buffer.SelectionStart.Y) { + rLineX += (selStartCol - ScrollX) * fe.MaxXAdvance; + rLineW -= selStartCol * fe.MaxXAdvance; + } + if (lineIndex == buffer.SelectionEnd.Y) + rLineW -= (lstr.Length - selEndCol) * fe.MaxXAdvance; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); + gr.SetSourceColor (SelectionBackground); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSourceColor (SelectionForeground); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + } + void drawParsedCodeLine (Context gr, double x, double y, int i, int lineIndex) { + int lPtr = 0; + CodeLine cl = PrintedLines[i]; + + for (int t = 0; t < cl.Tokens.Count; t++) { + string lstr = cl.Tokens [t].PrintableContent; if (lPtr < ScrollX) { if (lPtr - ScrollX + lstr.Length <= 0) { lPtr += lstr.Length; @@ -767,8 +666,8 @@ namespace Crow.Coding FontSlant fts = FontSlant.Normal; FontWeight ftw = FontWeight.Normal; - if (formatting.ContainsKey ((int)tokens [t].Type)) { - TextFormatting tf = formatting [(int)tokens [t].Type]; + if (formatting.ContainsKey ((int)cl.Tokens [t].Type)) { + TextFormatting tf = formatting [(int)cl.Tokens [t].Type]; bg = tf.Background; fg = tf.Foreground; if (tf.Bold) @@ -780,26 +679,24 @@ namespace Crow.Coding gr.SelectFontFace (Font.Name, fts, ftw); gr.SetSourceColor (fg); - int x = leftMargin + cb.X + (int)((lPtr - ScrollX) * fe.MaxXAdvance); - gr.MoveTo (x, y + fe.Ascent); gr.ShowText (lstr); gr.Fill (); - if (selectionInProgress && lineIndex >= selectionStart.Y && lineIndex <= selectionEnd.Y && - !(lineIndex == selectionStart.Y && lPtr + lstr.Length <= selectionStart.X) && - !(lineIndex == selectionEnd.Y && selectionEnd.X <= lPtr)) { + if (buffer.SelectionInProgress && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y && + !(lineIndex == buffer.SelectionStart.Y && lPtr + lstr.Length <= selStartCol) && + !(lineIndex == buffer.SelectionEnd.Y && selEndCol <= lPtr)) { double rLineX = x, - rLineY = cb.Y + i * fe.Height, + rLineY = y, rLineW = lstr.Length * fe.MaxXAdvance; double startAdjust = 0.0; - if ((lineIndex == selectionStart.Y) && (selectionStart.X < lPtr + lstr.Length) && (selectionStart.X > lPtr)) - startAdjust = (selectionStart.X - lPtr) * fe.MaxXAdvance; + if ((lineIndex == buffer.SelectionStart.Y) && (selStartCol < lPtr + lstr.Length) && (selStartCol > lPtr)) + startAdjust = (selStartCol - lPtr) * fe.MaxXAdvance; rLineX += startAdjust; - if ((lineIndex == selectionEnd.Y) && (selectionEnd.X < lPtr + lstr.Length)) - rLineW = (selectionEnd.X - lPtr) * fe.MaxXAdvance; + if ((lineIndex == buffer.SelectionEnd.Y) && (selEndCol < lPtr + lstr.Length)) + rLineW = (selEndCol - lPtr) * fe.MaxXAdvance; rLineW -= startAdjust; gr.Save (); @@ -810,15 +707,16 @@ namespace Crow.Coding gr.Clip (); gr.Operator = Operator.Over; gr.SetSourceColor (selfg); - gr.MoveTo (x, cb.Y + fe.Ascent + fe.Height * i); + gr.MoveTo (x, y + fe.Ascent); gr.ShowText (lstr); gr.Fill (); gr.Restore (); } - + x += (int)lstr.Length * fe.MaxXAdvance; lPtr += lstr.Length; } } + #endregion #region GraphicObject overrides @@ -856,46 +754,90 @@ namespace Crow.Coding updateVisibleColumns (); } + int selStartCol; + int selEndCol; + protected override void onDraw (Context gr) { +// if (!System.Threading.Monitor.TryEnter (buffer.EditMutex)) { +// RegisterForGraphicUpdate (); +// return; +// } + base.onDraw (gr); - if (parser != null) - drawParsed (gr); - else - draw(gr); + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + gr.FontOptions = Interface.FontRenderingOptions; + gr.Antialias = Interface.Antialias; + Rectangle cb = ClientRectangle; + + Foreground.SetAsSource (gr); + + lock (buffer.EditMutex) { + #region draw text cursor + if (buffer.SelectionInProgress){ + selStartCol = getTabulatedColumn (buffer.SelectionStart); + selEndCol = getTabulatedColumn (buffer.SelectionEnd); + }else if (HasFocus){ + gr.LineWidth = 1.0; + double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance + leftMargin; + gr.MoveTo (0.5 + cursorX, cb.Y + (printedCurrentLine) * fe.Height); + gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); + gr.Stroke(); + } + #endregion + + if (PrintedLines != null) { + for (int i = 0; i < visibleLines; i++) { + if (i + ScrollY >= buffer.UnfoldedLines)//TODO:need optimize + break; + drawLine (gr, cb, i); + } + } + } + //System.Threading.Monitor.Exit (buffer.EditMutex); } #endregion #region Mouse handling Point mouseLocalPos; + bool doubleClicked = false; void updateCurrentPos(){ - if (mouseLocalPos.X < 0) - CurrentColumn--; - else - CurrentColumn = ScrollX + (int)Math.Round ((mouseLocalPos.X - leftMargin) / fe.MaxXAdvance); - +// if (mouseLocalPos.X < 0) +// CurrentColumn--; +// else PrintedCurrentLine = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height)); + int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X - leftMargin) / fe.MaxXAdvance); - if (mouseLocalPos.Y < 0) - ScrollY--; + int i = 0; + int buffCol = 0; + while (i < curVisualCol && buffCol < buffer.CurrentCodeLine.Length) { + if (buffer.CurrentCodeLine[buffCol] == '\t') + i += Interface.TabSize; + else + i++; + buffCol++; + } + buffer.CurrentColumn = buffCol; - CurrentPosition = buffer.TabulatedPosition; //for rounding if in middle of tabs +// if (mouseLocalPos.Y < 0) +// ScrollY--; } public override void onMouseEnter (object sender, MouseMoveEventArgs e) { base.onMouseEnter (sender, e); if (e.X - ScreenCoordinates(Slot).X < leftMargin + ClientRectangle.X) - currentInterface.MouseCursor = XCursor.Default; + CurrentInterface.MouseCursor = XCursor.Default; else - currentInterface.MouseCursor = XCursor.Text; + CurrentInterface.MouseCursor = XCursor.Text; } public override void onMouseLeave (object sender, MouseMoveEventArgs e) { base.onMouseLeave (sender, e); - currentInterface.MouseCursor = XCursor.Default; + CurrentInterface.MouseCursor = XCursor.Default; } public override void onMouseMove (object sender, MouseMoveEventArgs e) { @@ -905,20 +847,18 @@ namespace Crow.Coding if (!e.Mouse.IsButtonDown (MouseButton.Left)) { if (mouseLocalPos.X < leftMargin) - currentInterface.MouseCursor = XCursor.Default; + CurrentInterface.MouseCursor = XCursor.Default; else - currentInterface.MouseCursor = XCursor.Text; + CurrentInterface.MouseCursor = XCursor.Text; return; } - if (!HasFocus || SelBegin < 0) + if (!HasFocus || !buffer.SelectionInProgress) return; //mouse is down - updateCurrentPos (); - SelRelease = CurrentPosition; - - RegisterForRedraw(); + updateCurrentPos(); + buffer.SetSelEndPos (); } public override void onMouseDown (object sender, MouseButtonEventArgs e) { @@ -934,36 +874,35 @@ namespace Crow.Coding } if (mouseLocalPos.X < leftMargin) { - toogleFolding (parser.Tokens.IndexOf (PrintedLines[(int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height))])); - } else { - updateCurrentPos (); - SelBegin = SelRelease = CurrentPosition; - } - RegisterForRedraw(); + toogleFolding (buffer.IndexOf (PrintedLines [(int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height))])); + return; + } + + updateCurrentPos (); + buffer.SetSelStartPos (); } public override void onMouseUp (object sender, MouseButtonEventArgs e) { - Debug.WriteLine ("MouseUp"); base.onMouseUp (sender, e); - if (SelBegin == SelRelease) - SelBegin = SelRelease = -1; - - updateCurrentPos (); - RegisterForRedraw (); + if (buffer.SelectionIsEmpty) + buffer.ResetSelection (); } - bool doubleClicked = false; + public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e) { doubleClicked = true; - Debug.WriteLine ("DoubleClick"); base.onMouseDoubleClick (sender, e); - GotoWordStart (); - SelBegin = CurrentPosition; - GotoWordEnd (); - SelRelease = CurrentPosition; - RegisterForRedraw (); + buffer.GotoWordStart (); + buffer.SetSelStartPos (); + buffer.GotoWordEnd (); + buffer.SetSelEndPos (); + } + + public override void onMouseWheel (object sender, MouseWheelEventArgs e) + { + base.onMouseWheel (sender, e); } #endregion @@ -977,119 +916,117 @@ namespace Crow.Coding switch (key) { case Key.Back: - if (CurrentPosition == 0) - return; - this.DeleteChar(); + buffer.DeleteChar (); break; case Key.Clear: break; case Key.Delete: - if (selectionIsEmpty) { + if (buffer.SelectionIsEmpty) MoveRight (); - }else if (e.Shift) - currentInterface.Clipboard = this.SelectedText; - this.DeleteChar (); + else if (e.Shift) + CurrentInterface.Clipboard = buffer.SelectedText; + buffer.DeleteChar (); break; case Key.Enter: case Key.KeypadEnter: - if (!selectionIsEmpty) - this.DeleteChar (); + if (!buffer.SelectionIsEmpty) + buffer.DeleteChar (); buffer.InsertLineBreak (); break; case Key.Escape: - SelRelease = SelBegin = -1; + buffer.ResetSelection (); break; case Key.Home: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = new Point (CurrentColumn, CurrentLine); + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); if (e.Control) - CurrentLine = 0; - CurrentColumn = 0; - SelRelease = new Point (CurrentColumn, CurrentLine); + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + buffer.SetSelEndPos (); break; } - SelRelease = SelBegin = -1; + buffer.ResetSelection (); if (e.Control) - CurrentLine = 0; - CurrentColumn = 0; + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; break; case Key.End: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = CurrentPosition; + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); if (e.Control) - CurrentLine = int.MaxValue; - CurrentColumn = int.MaxValue; - SelRelease = CurrentPosition; + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + buffer.SetSelEndPos (); break; } - SelRelease = SelBegin = -1; + buffer.ResetSelection (); if (e.Control) - CurrentLine = int.MaxValue; - CurrentColumn = int.MaxValue; + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; break; case Key.Insert: if (e.Shift) - this.Insert (currentInterface.Clipboard); - else if (e.Control && !selectionIsEmpty) - currentInterface.Clipboard = this.SelectedText; + buffer.Insert (CurrentInterface.Clipboard); + else if (e.Control && !buffer.SelectionIsEmpty) + CurrentInterface.Clipboard = buffer.SelectedText; break; case Key.Left: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = CurrentPosition; + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); if (e.Control) - GotoWordStart (); + buffer.GotoWordStart (); else MoveLeft (); - SelRelease = CurrentPosition; + buffer.SetSelEndPos (); break; } - SelRelease = SelBegin = -1; + buffer.ResetSelection (); if (e.Control) - GotoWordStart (); + buffer.GotoWordStart (); else MoveLeft(); break; case Key.Right: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = CurrentPosition; + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); if (e.Control) - GotoWordEnd (); + buffer.GotoWordEnd (); else MoveRight (); - SelRelease = CurrentPosition; + buffer.SetSelEndPos (); break; } - SelRelease = SelBegin = -1; + buffer.ResetSelection (); if (e.Control) - GotoWordEnd (); + buffer.GotoWordEnd (); else MoveRight (); break; case Key.Up: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = CurrentPosition; - MoveUp (); - SelRelease = CurrentPosition; + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine--; + buffer.SetSelEndPos (); break; } - SelRelease = SelBegin = -1; - MoveUp (); + buffer.ResetSelection (); + PrintedCurrentLine--; break; case Key.Down: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = CurrentPosition; - MoveDown (); - SelRelease = CurrentPosition; + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine++; + buffer.SetSelEndPos (); break; } - SelRelease = SelBegin = -1; - MoveDown (); + buffer.ResetSelection (); + PrintedCurrentLine++; break; case Key.Menu: break; @@ -1097,32 +1034,33 @@ namespace Crow.Coding break; case Key.PageDown: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = CurrentPosition; - CurrentLine += visibleLines; - SelRelease = CurrentPosition; + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine += visibleLines; + buffer.SetSelEndPos (); break; } - SelRelease = -1; - CurrentLine += visibleLines; + buffer.ResetSelection (); + PrintedCurrentLine += visibleLines; break; case Key.PageUp: if (e.Shift) { - if (selectionIsEmpty) - SelBegin = CurrentPosition; - CurrentLine -= visibleLines; - SelRelease = CurrentPosition; + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine -= visibleLines; + buffer.SetSelEndPos (); break; } - CurrentLine -= visibleLines; + buffer.ResetSelection (); + PrintedCurrentLine -= visibleLines; break; case Key.RWin: break; case Key.Tab: - this.Insert ("\t"); + buffer.Insert ("\t"); break; case Key.F8: - toogleFolding (CurrentLine); + toogleFolding (buffer.CurrentLine); // if (parser != null) // reparseSource (); break; @@ -1135,12 +1073,10 @@ namespace Crow.Coding { base.onKeyPress (sender, e); - this.Insert (e.KeyChar.ToString()); - - SelRelease = -1; - SelBegin = -1; //new Point(CurrentColumn, SelBegin.Y); + buffer.Insert (e.KeyChar.ToString()); + buffer.ResetSelection (); - RegisterForGraphicUpdate(); + //RegisterForGraphicUpdate(); } #endregion } diff --git a/src/TokenList.cs b/src/TokenList.cs deleted file mode 100644 index f4d03c7..0000000 --- a/src/TokenList.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Crow.Coding -{ - public class TokenList : List - { - /// The dirty state indicate that this line has changed and should be reparsed - public bool Dirty = true; - /// - /// The state of the parser when end of line was reached, used to setup initial state for next line parsing - /// - public int EndingState; - /// - /// Folding state reside here because it's the highest level of abstraction line per line - /// - public bool folded = false; - public Node SyntacticNode = null; - /// - /// if parsing issue error, exception is not null and tokenlist should contains only one token with line content and type = unknown - /// - public ParsingException exception = null; - - public TokenList () : base () - { - EndingState = 0; - } - /// - /// Initializes an in error source line - /// - public TokenList (ParsingException ex, string rawLineTxt){ - exception = ex; - this.Add (new Token () { Content = rawLineTxt }); - } - - public int FirstNonBlankTokenIndex { - get { - for (int i = 0; i < this.Count; i++) { - if (this [i].Type != Parser.TokenType.WhiteSpace && this [i].Type != Parser.TokenType.Unknown) - return i; - } - return -1; - } - } - - //override list.clear to clear additional states of tokenList - public new void Clear() { - EndingState = 0; - SyntacticNode = null; - exception = null; - Dirty = true; - base.Clear (); - } - } -} - diff --git a/src/XMLParser.cs b/src/XMLParser.cs index dcb826a..2a8ffeb 100644 --- a/src/XMLParser.cs +++ b/src/XMLParser.cs @@ -14,7 +14,9 @@ namespace Crow.Coding WhiteSpace = Parser.TokenType.WhiteSpace, NewLine = Parser.TokenType.NewLine, LineComment = Parser.TokenType.LineComment, + BlockCommentStart = Parser.TokenType.BlockCommentStart, BlockComment = Parser.TokenType.BlockComment, + BlockCommentEnd = Parser.TokenType.BlockCommentEnd, Affectation = Parser.TokenType.Affectation, XMLDecl = Parser.TokenType.Preprocessor, ElementStart, @@ -83,32 +85,28 @@ namespace Crow.Coding public override void SetLineInError (ParsingException ex) { base.SetLineInError (ex); - Tokens[ex.Line].EndingState = (int)States.init; + //buffer[ex.Line].Tokens.EndingState = (int)States.init; } - public override void Parse (int line) + public override void ParseCurrentLine () { - Debug.WriteLine (string.Format("parsing line:{0}", line)); - - currentLine = line; - currentColumn = 0; - eof = false; - bool eol = false; - TokensLine = Tokens [line]; + Debug.WriteLine (string.Format("parsing line:{0}", currentLine)); + CodeLine cl = buffer [currentLine]; + cl.Tokens = new List (); //retrieve current parser state from previous line - if (line > 0) - curState = (States)Tokens [line - 1].EndingState; + if (currentLine > 0) + curState = (States)buffer[currentLine - 1].EndingState; else curState = States.init; - States previousEndingState = (States)TokensLine.EndingState; - TokensLine.Clear (); + States previousEndingState = (States)cl.EndingState; + - while (! (eof||eol)) { + while (! eol) { SkipWhiteSpaces (); - if (eof) + if (eol) break; if (Peek () == '\n') { @@ -247,11 +245,10 @@ namespace Crow.Coding } } - TokensLine.EndingState = (int)curState; - TokensLine.Dirty = false; + if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1) + buffer [currentLine + 1].Tokens = null; - if (previousEndingState != curState && line < Tokens.Count - 1) - Tokens [line + 1].Dirty = true; + cl.EndingState = (int)curState; } public override void SyntaxAnalysis () @@ -260,28 +257,31 @@ namespace Crow.Coding Node currentNode = RootNode; - for (int i = 0; i < Tokens.Count; i++) { - TokenList curTL = Tokens [i]; - curTL.SyntacticNode = null; + for (int i = 0; i < buffer.LineCount; i++) { + CodeLine cl = buffer[i]; + if (cl.Tokens == null) + continue; + cl.SyntacticNode = null; int tokPtr = 0; - while (tokPtr < curTL.Count) { - switch ((XMLParser.TokenType)curTL [tokPtr].Type) { + while (tokPtr < cl.Tokens.Count) { + switch ((XMLParser.TokenType)cl.Tokens [tokPtr].Type) { case TokenType.ElementStart: tokPtr++; - Node newElt = new Node () { Name = curTL [tokPtr].Content, StartLine = i }; + Node newElt = new Node () { Name = cl.Tokens [tokPtr].Content, StartLine = cl }; currentNode.AddChild (newElt); currentNode = newElt; - if (curTL.SyntacticNode == null) - curTL.SyntacticNode = newElt; + if (cl.SyntacticNode == null) + cl.SyntacticNode = newElt; break; case TokenType.ElementEnd: tokPtr++; - if (tokPtr < curTL.Count) { - if ((XMLParser.TokenType)curTL [tokPtr].Type == TokenType.ElementName && curTL [tokPtr].Content != currentNode.Name) + if (tokPtr < cl.Tokens.Count) { + if ((XMLParser.TokenType)cl.Tokens [tokPtr].Type == TokenType.ElementName && + cl.Tokens [tokPtr].Content != currentNode.Name) throw new ParsingException (this, "Closing tag mismatch"); } - currentNode.EndLine = i; + currentNode.EndLine = cl; currentNode = currentNode.Parent; break; case TokenType.ElementClosing: