aboutsummaryrefslogtreecommitdiffstatsci
path: root/CrowIDE/src/SourceEditor
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2018-02-24 11:51:12 +0100
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2018-02-24 11:51:12 +0100
commitaf3a87ef16e2f4df9fa01a89fd709125c91188d6 (patch)
tree506b54417e5e9f2ccb8e17b90642ad94060ccc91 /CrowIDE/src/SourceEditor
parent1293bc896bdbc6819999d17bda8286a6e1a792b0 (diff)
change namespace of CrowIDE to Crow.Coding, move editor source inside, crowIde wip
Diffstat (limited to 'CrowIDE/src/SourceEditor')
-rw-r--r--CrowIDE/src/SourceEditor/CSharpParser.cs45
-rw-r--r--CrowIDE/src/SourceEditor/CodeBuffer.cs454
-rw-r--r--CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs20
-rw-r--r--CrowIDE/src/SourceEditor/CodeLine.cs76
-rw-r--r--CrowIDE/src/SourceEditor/Node.cs32
-rw-r--r--CrowIDE/src/SourceEditor/Parser.cs297
-rw-r--r--CrowIDE/src/SourceEditor/ParsingException.cs22
-rw-r--r--CrowIDE/src/SourceEditor/SourceEditor.cs1107
-rw-r--r--CrowIDE/src/SourceEditor/TextFormatting.cs19
-rw-r--r--CrowIDE/src/SourceEditor/Token.cs70
-rw-r--r--CrowIDE/src/SourceEditor/XMLParser.cs299
11 files changed, 2441 insertions, 0 deletions
diff --git a/CrowIDE/src/SourceEditor/CSharpParser.cs b/CrowIDE/src/SourceEditor/CSharpParser.cs
new file mode 100644
index 00000000..7d2e28e5
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/CSharpParser.cs
@@ -0,0 +1,45 @@
+using System;
+using Crow;
+
+namespace Crow.Coding
+{
+ public class CSharpParser : Parser
+ {
+ public new enum TokenType {
+ Unknown = Parser.TokenType.Unknown,
+ WhiteSpace = Parser.TokenType.WhiteSpace,
+ LineComment = Parser.TokenType.LineComment,
+ BlockComment = Parser.TokenType.BlockComment,
+ OpenParenth,
+ CloseParenth,
+ OpenBlock,
+ CloseBlock,
+ StatementEnding,
+ UnaryOp,
+ BinaryOp,
+ Affectation,
+ StringLiteral,
+ CharacterLiteral,
+ DigitalLiteral,
+ Literal,
+ Identifier,
+ Indexer,
+ Type,
+ Preprocessor,
+ }
+
+ public CSharpParser (CodeBuffer _buffer) : base(_buffer)
+ {
+ }
+
+ public override void ParseCurrentLine ()
+ {
+ throw new NotImplementedException ();
+ }
+ public override void SyntaxAnalysis ()
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
+
diff --git a/CrowIDE/src/SourceEditor/CodeBuffer.cs b/CrowIDE/src/SourceEditor/CodeBuffer.cs
new file mode 100644
index 00000000..399d10ab
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/CodeBuffer.cs
@@ -0,0 +1,454 @@
+//
+// CodeTextBuffer.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2017 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+
+namespace Crow.Coding
+{
+ /// <summary>
+ /// Code buffer, lines are arranged in a List<string>, new line chars are removed during string.split on '\n...',
+ /// </summary>
+ 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<CodeBufferEventArgs> LineUpadateEvent;
+ public event EventHandler<CodeBufferEventArgs> LineRemoveEvent;
+ public event EventHandler<CodeBufferEventArgs> LineAdditionEvent;
+ public event EventHandler<CodeBufferEventArgs> FoldingEvent;
+ public event EventHandler BufferCleared;
+ public event EventHandler SelectionChanged;
+ public event EventHandler PositionChanged;
+ #endregion
+
+ #region CTOR
+ public CodeBuffer () {
+
+ }
+ #endregion
+
+ string lineBreak = Interface.LineBreak;
+ List<CodeLine> lines = new List<CodeLine>();
+ 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 LineCount { get { return lines.Count;}}
+ public int IndexOf (CodeLine cl) {
+ return lines.IndexOf (cl);
+ }
+
+ public CodeLine this[int i]
+ {
+ get { return lines[i]; }
+ set {
+ if (lines [i] == value)
+ return;
+ lock (EditMutex) {
+ lines [i] = value;
+ LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ }
+ }
+
+ public void RemoveAt(int i){
+ lock (EditMutex) {
+ lines.RemoveAt (i);
+ LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ }
+ public void Insert(int i, string item){
+ lock (EditMutex) {
+ lines.Insert (i, item);
+ LineAdditionEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ }
+ 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;
+ 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 () {
+ 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();
+
+ if (string.IsNullOrEmpty (rawSource))
+ return;
+
+ AddRange (Regex.Split (rawSource, "\r\n|\r|\n|\\\\n"));
+
+ lineBreak = detectLineBreakKind (rawSource);
+ }
+
+ /// <summary>
+ /// Finds the longest visual line as printed on screen with tabulation replaced with n spaces
+ /// </summary>
+ public void FindLongestVisualLine(){
+ longestLineCharCount = 0;
+ for (int i = 0; i < this.LineCount; i++) {
+ if (lines[i].PrintableLength > longestLineCharCount) {
+ longestLineCharCount = lines[i].PrintableLength;
+ longestLineIdx = i;
+ }
+ }
+ Debug.WriteLine ("Longest line: {0}->{1}", longestLineIdx, longestLineCharCount);
+ }
+ /// <summary> line break could be '\r' or '\n' or '\r\n' </summary>
+ static string detectLineBreakKind(string buffer){
+ string strLB = "";
+
+ if (string.IsNullOrEmpty(buffer))
+ return Interface.LineBreak;
+ int i = 0;
+ while ( i < buffer.Length) {
+ if (buffer [i] == '\r') {
+ strLB += '\r';
+ i++;
+ }
+ if (i < buffer.Length) {
+ if (buffer [i] == '\r')
+ return "\r";
+ if (buffer[i] == '\n')
+ strLB += '\n';
+ }
+ if (!string.IsNullOrEmpty (strLB))
+ return strLB;
+ i++;
+ }
+ return Interface.LineBreak;
+ }
+ /// <summary>
+ /// return all lines with linebreaks
+ /// </summary>
+ public string FullText{
+ get {
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// unfolded and not in folds line count
+ /// </summary>
+ 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;
+ }
+ }
+
+ /// <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);
+ }
+
+ public int GetEndNodeIndex (int line) {
+ return IndexOf (this [line].SyntacticNode.EndLine);
+ }
+ /// <summary>
+ /// Gets visual position computed from actual buffer position
+ /// </summary>
+// public Point TabulatedPosition {
+// get { return new Point (TabulatedColumn, _currentLine); }
+// }
+ /// <summary>
+ /// set buffer current position from visual position
+ /// </summary>
+// 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 void SetSelEndPos () {
+ selEndPos = CurrentPosition;
+ SelectionChanged.Raise (this, null);
+ }
+ /// <summary>
+ /// Set selection in buffer to -1, empty selection
+ /// </summary>
+ public void ResetSelection () {
+ selStartPos = selEndPos = -1;
+ SelectionChanged.Raise (this, null);
+ }
+
+ public string SelectedText {
+ get {
+ if (SelectionIsEmpty)
+ return "";
+ 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 [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 [selEnd.Y].Content.Substring (0, selEnd.X);
+ return tmp;
+ }
+ }
+ /// <summary>
+ /// ordered selection start and end positions in char units
+ /// </summary>
+ public Point SelectionStart {
+ get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos :
+ selStartPos.Y > selEndPos.Y ? selEndPos :
+ selStartPos.X < selEndPos.X ? selStartPos : selEndPos; }
+ }
+ 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; } }
+ /// <summary>
+ /// Current column in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ 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;
+
+ PositionChanged.Raise (this, null);
+ }
+ }
+ /// <summary>
+ /// Current row in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ 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;
+
+ 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]; }
+ }
+ /// <summary>
+ /// Current position in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ public Point CurrentPosition {
+ get { return new Point(CurrentColumn, CurrentLine); }
+// set {
+// _currentCol = value.X;
+// _currentLine = value.Y;
+// }
+ }
+ /// <summary>
+ /// get char at current position in buffer
+ /// </summary>
+ protected Char CurrentChar { get { return lines [CurrentLine] [CurrentColumn]; } }
+
+ 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()
+ {
+ 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;
+ }
+ CurrentColumn--;
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Remove (CurrentColumn, 1));
+ } else {
+ int linesToRemove = SelectionEnd.Y - SelectionStart.Y + 1;
+ int l = SelectionStart.Y;
+
+ 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 ();
+ }
+ }
+ }
+ /// <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();
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[0]));
+ CurrentColumn += strLines[0].Length;
+ for (int i = 1; i < strLines.Length; i++) {
+ InsertLineBreak ();
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[i]));
+ CurrentColumn += strLines[i].Length;
+ }
+ }
+ /// <summary>
+ /// Insert a line break.
+ /// </summary>
+ public void InsertLineBreak()
+ {
+ if (CurrentColumn > 0) {
+ Insert (CurrentLine + 1, this [CurrentLine].Content.Substring (CurrentColumn));
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Substring (0, CurrentColumn));
+ } else
+ Insert(CurrentLine, "");
+
+ CurrentColumn = 0;
+ CurrentLine++;
+ }
+ #endregion
+ }
+}
+
diff --git a/CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs b/CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs
new file mode 100644
index 00000000..07dd25b6
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Crow.Coding
+{
+ public class CodeBufferEventArgs : EventArgs {
+ public int LineStart;
+ public int LineCount;
+
+ public CodeBufferEventArgs(int lineNumber) {
+ LineStart = lineNumber;
+ LineCount = 1;
+ }
+ public CodeBufferEventArgs(int lineStart, int lineCount) {
+ LineStart = lineStart;
+ LineCount = lineCount;
+ }
+ }
+
+}
+
diff --git a/CrowIDE/src/SourceEditor/CodeLine.cs b/CrowIDE/src/SourceEditor/CodeLine.cs
new file mode 100644
index 00000000..622584c7
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/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<Token> 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/CrowIDE/src/SourceEditor/Node.cs b/CrowIDE/src/SourceEditor/Node.cs
new file mode 100644
index 00000000..24818e01
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/Node.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+
+namespace Crow.Coding
+{
+ public class Node
+ {
+ public Node Parent;
+ public string Name;
+ public string Type;
+ public CodeLine StartLine;
+ public CodeLine EndLine;
+ public Dictionary<string,string> Attributes = new Dictionary<string, string> ();
+
+ public List<Node> Children = new List<Node>();
+
+ public Node ()
+ {
+ }
+
+ public void AddChild (Node child) {
+ child.Parent = this;
+ Children.Add (child);
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("Name:{0}, Type:{1}\n\tparent:{2}", Name, Type, Parent);
+ }
+ }
+}
+
diff --git a/CrowIDE/src/SourceEditor/Parser.cs b/CrowIDE/src/SourceEditor/Parser.cs
new file mode 100644
index 00000000..79dc086f
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/Parser.cs
@@ -0,0 +1,297 @@
+using System;
+using System.IO;
+using Crow;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Crow.Coding
+{
+ /// <summary>
+ /// base class for tokenizing sources
+ /// </summary>
+ public abstract class Parser
+ {
+ /// <summary>
+ /// Default tokens, this enum may be overriden in derived parser with the new keyword,
+ /// see XMLParser for example.
+ /// </summary>
+ public enum TokenType {
+ Unknown,
+ WhiteSpace,
+ NewLine,
+ LineComment,
+ BlockCommentStart,
+ BlockComment,
+ BlockCommentEnd,
+ Type,
+ Identifier,
+ Indexer,
+ OpenBlock,
+ CloseBlock,
+ StatementEnding,
+ UnaryOp,
+ BinaryOp,
+ Affectation,
+ StringLitteralOpening,
+ StringLitteralClosing,
+ StringLitteral,
+ NumericLitteral,
+ Preprocessor,
+ }
+
+ #region CTOR
+ public Parser (CodeBuffer _buffer)
+ {
+ buffer = _buffer;
+
+ buffer.LineUpadateEvent += Buffer_LineUpadateEvent;
+ //buffer.LineAdditionEvent += Buffer_LineAdditionEvent;;
+ buffer.LineRemoveEvent += Buffer_LineRemoveEvent;
+ buffer.BufferCleared += Buffer_BufferCleared;
+ }
+
+ #endregion
+
+ #region Buffer events handlers
+ void Buffer_BufferCleared (object sender, EventArgs e)
+ {
+
+ }
+ void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e)
+ {
+
+ }
+ void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e)
+ {
+ reparseSource ();
+ }
+ void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e)
+ {
+ for (int i = 0; i < e.LineCount; i++)
+ tryParseBufferLine (e.LineStart + i);
+ reparseSource ();
+ }
+ #endregion
+
+ void updateFolding () {
+ // Stack<TokenList> foldings = new Stack<TokenList>();
+ // bool inStartTag = false;
+ //
+ // for (int i = 0; i < parser.Tokens.Count; i++) {
+ // TokenList tl = parser.Tokens [i];
+ // tl.foldingTo = null;
+ // int fstTK = tl.FirstNonBlankTokenIndex;
+ // if (fstTK > 0 && fstTK < tl.Count - 1) {
+ // if (tl [fstTK + 1] != XMLParser.TokenType.ElementName)
+ // continue;
+ // if (tl [fstTK] == XMLParser.TokenType.ElementStart) {
+ // //search closing tag
+ // int tkPtr = fstTK+2;
+ // while (tkPtr < tl.Count) {
+ // if (tl [tkPtr] == XMLParser.TokenType.ElementClosing)
+ //
+ // tkPtr++;
+ // }
+ // if (tl.EndingState == (int)XMLParser.States.Content)
+ // foldings.Push (tl);
+ // else if (tl.EndingState == (int)XMLParser.States.StartTag)
+ // inStartTag = true;
+ // continue;
+ // }
+ // if (tl [fstTK] == XMLParser.TokenType.ElementEnd) {
+ // TokenList tls = foldings.Pop ();
+ // int fstTKs = tls.FirstNonBlankTokenIndex;
+ // if (tls [fstTK + 1].Content == tl [fstTK + 1].Content) {
+ // tl.foldingTo = tls;
+ // continue;
+ // }
+ // parser.CurrentPosition = tls [fstTK + 1].Start;
+ // parser.SetLineInError(new ParsingException(parser, "closing tag not corresponding"));
+ // }
+ //
+ // }
+ // }
+ }
+ public void reparseSource () {
+ for (int i = 0; i < buffer.LineCount; i++) {
+ if (!buffer[i].IsParsed)
+ tryParseBufferLine (i);
+ }
+ try {
+ SyntaxAnalysis ();
+ } catch (Exception ex) {
+ Debug.WriteLine ("Syntax Error: " + ex.ToString ());
+ if (ex is ParsingException)
+ SetLineInError (ex as ParsingException);
+ }
+ }
+ public void tryParseBufferLine(int lPtr) {
+ buffer [lPtr].exception = null;
+ currentLine = lPtr;
+ currentColumn = 0;
+ eol = false;
+
+ try {
+ ParseCurrentLine ();
+ } catch (Exception ex) {
+ Debug.WriteLine (ex.ToString ());
+ if (ex is ParsingException)
+ SetLineInError (ex as ParsingException);
+ }
+
+ }
+
+ protected CodeBuffer buffer;
+
+ internal int currentLine = 0;
+ internal int currentColumn = 0;
+ protected Token currentTok;
+ protected bool eol = true;
+
+ public Node RootNode;
+
+ protected Point CurrentPosition {
+ get { return new Point (currentLine, currentColumn); }
+ set {
+ currentLine = value.Y;
+ currentColumn = value.X;
+ }
+ }
+
+ public abstract void ParseCurrentLine();
+ public abstract void SyntaxAnalysis ();
+
+ public virtual void SetLineInError(ParsingException ex) {
+ currentTok = default(Token);
+ if (ex.Line >= 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
+ /// <summary>
+ /// Read one char from current position in buffer and store it into the current token
+ /// </summary>
+ /// <param name="startOfTok">if true, set the Start position of the current token to the current position</param>
+ protected void readToCurrTok(bool startOfTok = false){
+ if (startOfTok)
+ currentTok.Start = CurrentPosition;
+ currentTok += Read();
+ }
+ /// <summary>
+ /// read n char from the buffer and store it into the current token
+ /// </summary>
+ protected void readToCurrTok(int length) {
+ for (int i = 0; i < length; i++)
+ currentTok += Read ();
+ }
+ /// <summary>
+ /// Save current token into current TokensLine and raz current token
+ /// </summary>
+ protected void saveAndResetCurrentTok() {
+ currentTok.End = CurrentPosition;
+ buffer[currentLine].Tokens.Add (currentTok);
+ currentTok = default(Token);
+ }
+ /// <summary>
+ /// read one char and add current token to current TokensLine, current token is reset
+ /// </summary>
+ /// <param name="type">Type of the token</param>
+ /// <param name="startToc">set start of token to current position</param>
+ protected void readAndResetCurrentTok(System.Enum type, bool startToc = false) {
+ readToCurrTok ();
+ saveAndResetCurrentTok (type);
+ }
+ /// <summary>
+ /// Save current tok
+ /// </summary>
+ /// <param name="type">set the type of the tok</param>
+ protected void saveAndResetCurrentTok(System.Enum type) {
+ currentTok.Type = (TokenType)type;
+ saveAndResetCurrentTok ();
+ }
+ /// <summary>
+ /// Peek next char, emit '\n' if current column > buffer's line length
+ /// Throw error if eof is true
+ /// </summary>
+ protected virtual char Peek() {
+ if (eol)
+ throw new ParsingException (this, "Unexpected End of line");
+ return currentColumn < buffer [currentLine].Length ?
+ buffer [currentLine] [currentColumn] : '\n';
+ }
+ /// <summary>
+ /// Peek n char from buffer or less if remaining char in buffer's line is less than requested
+ /// if end of line is reached, no '\n' will be emitted, instead, empty string is returned. '\n' should be checked only
+ /// with single char Peek().
+ /// Throw error is eof is true
+ /// </summary>
+ /// <param name="length">Length.</param>
+ protected virtual string Peek(int length) {
+ 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].Content.Substring (currentColumn, lg);
+ }
+ /// <summary>
+ /// read one char from buffer at current position, if '\n' is read, current line is incremented
+ /// and column is reset to 0
+ /// </summary>
+ protected virtual char Read() {
+ char c = Peek ();
+ if (c == '\n')
+ eol = true;
+ currentColumn++;
+ return c;
+ }
+ /// <summary>
+ /// read until end of line is reached
+ /// </summary>
+ /// <returns>string read</returns>
+ protected virtual string ReadLine () {
+ string tmp = "";
+ while (!eol)
+ tmp += Read ();
+ return tmp;
+ }
+ /// <summary>
+ /// read until end expression is reached or end of line.
+ /// </summary>
+ /// <returns>string read minus the ending expression that has to be read after</returns>
+ /// <param name="endExp">Expression to search for</param>
+ protected virtual string ReadLineUntil (string endExp){
+ string tmp = "";
+
+ while (!eol) {
+ if (buffer [currentLine].Length - currentColumn - endExp.Length < 0) {
+ tmp += ReadLine();
+ break;
+ }
+ if (string.Equals (Peek (endExp.Length), endExp))
+ return tmp;
+ tmp += Read();
+ }
+ return tmp;
+ }
+ /// <summary>
+ /// skip white spaces, but not line break. Save spaces in a WhiteSpace token.
+ /// </summary>
+ 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 (!eol) {
+ if (!char.IsWhiteSpace (Peek ())||Peek()=='\n')
+ break;
+ readToCurrTok (currentTok.Type == TokenType.Unknown);
+ currentTok.Type = TokenType.WhiteSpace;
+ }
+ if (currentTok.Type != TokenType.Unknown)
+ saveAndResetCurrentTok ();
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/CrowIDE/src/SourceEditor/ParsingException.cs b/CrowIDE/src/SourceEditor/ParsingException.cs
new file mode 100644
index 00000000..88c51c9b
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/ParsingException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Crow.Coding
+{
+ public class ParsingException : Exception
+ {
+ public int Line;
+ public int Column;
+ public ParsingException(Parser parser, string txt)
+ : base(string.Format("Parser exception ({0},{1}): {2}", parser.currentLine, parser.currentColumn, txt))
+ {
+ Line = parser.currentLine;
+ Column = parser.currentColumn;
+ }
+ public ParsingException(Parser parser, string txt, Exception innerException)
+ : base(txt, innerException)
+ {
+ txt = string.Format("Parser exception ({0},{1}): {2}", parser.currentLine, parser.currentColumn, txt);
+ }
+ }
+}
+
diff --git a/CrowIDE/src/SourceEditor/SourceEditor.cs b/CrowIDE/src/SourceEditor/SourceEditor.cs
new file mode 100644
index 00000000..677ba8b7
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/SourceEditor.cs
@@ -0,0 +1,1107 @@
+//
+// ScrollingTextBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2013-2017 Jean-Philippe Bruyère
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.Collections;
+using Cairo;
+using System.Text;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Linq;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+
+namespace Crow.Coding
+{
+ /// <summary>
+ /// Scrolling text box optimized for monospace fonts, for coding
+ /// </summary>
+ public class SourceEditor : ScrollingObject
+ {
+ #region CTOR
+ 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));
+ formatting.Add ((int)XMLParser.TokenType.ElementStart, new TextFormatting (Color.Black, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.ElementEnd, new TextFormatting (Color.Black, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.ElementClosing, new TextFormatting (Color.Black, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.Affectation, new TextFormatting (Color.Black, Color.Transparent));
+
+ formatting.Add ((int)XMLParser.TokenType.AttributeValueOpening, new TextFormatting (Color.Carmine, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.AttributeValueClosing, new TextFormatting (Color.Carmine, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.AttributeValue, new TextFormatting (Color.OrangeRed, Color.Transparent, false, true));
+ formatting.Add ((int)XMLParser.TokenType.XMLDecl, new TextFormatting (Color.GreenCrayola, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.BlockComment, new TextFormatting (Color.Gray, Color.Transparent, false, true));
+
+ parsing.Add (".crow", "Crow.Coding.XMLParser");
+
+ buffer = new CodeBuffer ();
+ buffer.LineUpadateEvent += Buffer_LineUpadateEvent;
+ 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(""));
+
+ Thread updateSource = new Thread (updateSourceThreadFunc);
+ updateSource.IsBackground = true;
+ updateSource.Start ();
+ }
+ #endregion
+ string oldSource = "";
+ void updateSourceThreadFunc (){
+ while (true) {
+ if (projNode != null && buffer != null) {
+ string newSrc = buffer.FullText;
+ if (projNode.Source != newSrc)
+ projNode.Source = newSrc;
+ }
+ Thread.Sleep (100);
+ }
+ }
+ const int leftMarginGap = 3;//gap between items in margin and text
+ const int foldSize = 9;//folding rectangles size
+
+ #region private and protected fields
+ bool foldingEnabled = true;
+ ProjectFile projNode = null;
+ int leftMargin = 0; //margin used to display line numbers, folding errors,etc...
+ int visibleLines = 1;
+ int visibleColumns = 1;
+ CodeBuffer buffer;
+ Parser parser;
+ Color selBackground;
+ Color selForeground;
+// int _currentCol; //0 based cursor position in string
+// int _currentLine;
+
+
+ Dictionary<int, TextFormatting> formatting = new Dictionary<int, TextFormatting>();
+ Dictionary<string, string> parsing = new Dictionary<string, string>();
+
+ protected Rectangle rText;
+ protected FontExtents fe;
+ protected TextExtents te;
+ #endregion
+
+ void measureLeftMargin () {
+ leftMargin = 0;
+ if (PrintLineNumbers)
+ leftMargin += (int)Math.Ceiling((double)buffer.LineCount.ToString().Length * fe.MaxXAdvance) +6;
+ if (foldingEnabled)
+ leftMargin += foldSize;
+ if (leftMargin > 0)
+ leftMargin += leftMarginGap;
+ updateVisibleColumns ();
+ }
+ void findLongestLineAndUpdateMaxScrollX() {
+ buffer.FindLongestVisualLine ();
+ MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+ Debug.WriteLine ("SourceEditor: Find Longest line and update maxscrollx: {0} visible cols:{1}", MaxScrollX, visibleColumns);
+ }
+ /// <summary>
+ /// Updates visible line in widget, adapt max scroll y and updatePrintedLines
+ /// </summary>
+ void updateVisibleLines(){
+ visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height);
+ NotifyValueChanged ("VisibleLines", visibleLines);
+ updateMaxScrollY ();
+ updatePrintedLines ();
+ System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines);
+ System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY);
+ }
+ void updateVisibleColumns(){
+ visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width - leftMargin)/ fe.MaxXAdvance);
+ MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+
+ System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin);
+ System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX);
+ }
+ void updateMaxScrollY () {
+ if (parser == null || !foldingEnabled) {
+ MaxScrollY = Math.Max (0, buffer.LineCount - visibleLines);
+ if (buffer.UnfoldedLines > 0)
+ NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines);
+ } else {
+ MaxScrollY = Math.Max (0, buffer.UnfoldedLines - visibleLines);
+ if (buffer.UnfoldedLines > 0)
+ NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines);
+ }
+ }
+
+ int firstPrintedLine = -1;
+ /// <summary>
+ /// list of lines visible in the Editor depending on scrolling and folding
+ /// </summary>
+ List<CodeLine> PrintedLines;
+
+ void updatePrintedLines () {
+ lock (buffer.EditMutex) {
+ PrintedLines = new List<CodeLine> ();
+ 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 < buffer.LineCount) {
+ PrintedLines.Add (buffer [curL]);
+
+ if (buffer [curL].IsFolded)
+ curL = buffer.GetEndNodeIndex (curL);
+
+ curL++;
+ i++;
+ }
+ }
+ RegisterForGraphicUpdate ();
+ }
+ void toogleFolding (int line) {
+ if (parser == null || !foldingEnabled)
+ return;
+ buffer.ToogleFolding (line);
+ }
+
+ #region Buffer events handlers
+ void Buffer_BufferCleared (object sender, EventArgs e)
+ {
+ buffer.longestLineCharCount = 0;
+ 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[lptr].PrintableLength;
+ if (charCount > buffer.longestLineCharCount) {
+ buffer.longestLineIdx = lptr;
+ buffer.longestLineCharCount = charCount;
+ }else if (lptr <= buffer.longestLineIdx)
+ buffer.longestLineIdx++;
+ if (parser == null)
+ continue;
+ parser.tryParseBufferLine (e.LineStart + i);
+ }
+ measureLeftMargin ();
+
+ if (parser != null)
+ parser.reparseSource ();
+
+ updatePrintedLines ();
+ updateMaxScrollY ();
+ RegisterForGraphicUpdate ();
+ }
+ void Buffer_LineRemoveEvent (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;
+ }
+ 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[lptr].PrintableLength > buffer.longestLineCharCount) {
+ buffer.longestLineCharCount = buffer[lptr].PrintableLength;
+ buffer.longestLineIdx = lptr;
+ }
+ }
+ if (trigFindLongestLine)
+ 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) {
+ if (string.IsNullOrEmpty(extension))
+ return null;
+ if (!parsing.ContainsKey(extension))
+ return null;
+ Type parserType = Type.GetType (parsing [extension]);
+ if (parserType == null)
+ return null;
+ return (Parser)Activator.CreateInstance (parserType, buffer );
+ }
+
+ #region Public Crow Properties
+ [XmlAttributeAttribute]
+ public bool PrintLineNumbers
+ {
+ get { return Configuration.Global.Get<bool> ("PrintLineNumbers");
+ }
+ set
+ {
+ if (PrintLineNumbers == value)
+ return;
+ Configuration.Global.Set ("PrintLineNumbers", value);
+ NotifyValueChanged ("PrintLineNumbers", PrintLineNumbers);
+ measureLeftMargin ();
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute]
+ public ProjectFile ProjectNode
+ {
+ get {
+ return projNode;
+ }
+ set
+ {
+ if (projNode == value)
+ return;
+
+ projNode = value;
+
+ NotifyValueChanged ("ProjectNode", projNode);
+
+ if (projNode == null)
+ return;
+
+ if (!File.Exists (projNode.AbsolutePath))
+ return;
+
+ parser = getParserFromExt (System.IO.Path.GetExtension (projNode.Extension));
+
+ try {
+ buffer.Load (projNode.Source);
+ oldSource = projNode.Source;
+ } catch (Exception ex) {
+ Debug.WriteLine (ex.ToString ());
+ }
+
+ updateMaxScrollY ();
+ MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+ updatePrintedLines ();
+
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("BlueGray")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (value == selBackground)
+ return;
+ selBackground = value;
+ NotifyValueChanged ("SelectionBackground", selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (value == selForeground)
+ return;
+ selForeground = value;
+ NotifyValueChanged ("SelectionForeground", selForeground);
+ RegisterForRedraw ();
+ }
+ }
+
+ // [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);
+//// }
+// }
+// /// <summary>
+// /// 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
+// /// </summary>
+// /// <value>The current position.</value>
+// [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;
+////
+//// 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;
+// }
+// }
+
+ #endregion
+
+
+ void updateOnScreenCurLineFromBuffCurLine(){
+ printedCurrentLine = PrintedLines.IndexOf (buffer.CurrentCodeLine);
+ }
+
+// void setCurrentLineFromBuffer () {
+// _currentLine = buffer.CurrentLine;
+// NotifyValueChanged ("CurrentLine", _currentLine);
+// }
+
+ public override int ScrollY {
+ get {
+ return base.ScrollY;
+ }
+ set {
+ if (value == base.ScrollY)
+ return;
+ base.ScrollY = value;
+ updatePrintedLines ();
+ }
+ }
+
+
+ /// <summary>
+ /// Index of the currentline in the PrintedLines array
+ /// </summary>
+ int printedCurrentLine = 0;
+
+ /// <summary>
+ /// Current editor line, when set, update buffer.CurrentLine
+ /// </summary>
+ int PrintedCurrentLine {
+ get { return printedCurrentLine;}
+ set {
+ if (value < 0) {
+ ScrollY += value;
+ printedCurrentLine = 0;
+ } else if (PrintedLines.Count < visibleLines && value >= PrintedLines.Count) {
+ printedCurrentLine = PrintedLines.Count - 1;
+ }else if (value >= visibleLines) {
+ ScrollY += value - visibleLines + 1;
+ printedCurrentLine = visibleLines - 1;
+ }else
+ printedCurrentLine = value;
+ Debug.WriteLine ("printed current line:" + printedCurrentLine.ToString ());
+ //update position in buffer
+ buffer.CurrentLine = buffer.IndexOf (PrintedLines[printedCurrentLine]);
+ }
+ }
+ 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);
+ }
+ /// <summary>
+ /// Moves cursor one char to the left, move up if cursor reaches start of line
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ public bool MoveLeft(){
+ if (buffer.CurrentColumn == 0) {
+ if (printedCurrentLine == 0)
+ return false;
+ PrintedCurrentLine--;
+ buffer.CurrentColumn = int.MaxValue;
+ } else
+ buffer.CurrentColumn--;
+ return true;
+ }
+ /// <summary>
+ /// Moves cursor one char to the right, move down if cursor reaches end of line
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ 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;
+ }
+ #region Drawing
+ void drawLine(Context gr, Rectangle cb, int i) {
+ CodeLine cl = PrintedLines[i];
+ int lineIndex = buffer.IndexOf(cl);
+
+ 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 ((int)x, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling(fe.Height));
+ if (cl.exception != null) {
+ mgBg = Color.Red;
+ if (buffer.CurrentLine == lineIndex)
+ mgFg = Color.White;
+ else
+ mgFg = Color.LightGray;
+ }else if (buffer.CurrentLine == lineIndex) {
+ mgFg = Color.Black;
+ }
+ string strLN = (lineIndex+1).ToString ();
+ gr.SetSourceColor (mgBg);
+ gr.Rectangle (mgR);
+ gr.Fill();
+ gr.SetSourceColor (mgFg);
+
+ 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 (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 (cl.IsFolded) {
+ gr.MoveTo (rFld.Center.X + 0.5, rFld.Y + 2);
+ gr.LineTo (rFld.Center.X + 0.5, rFld.Bottom - 2);
+ }
+ gr.MoveTo (rFld.Left + 2, rFld.Center.Y + 0.5);
+ gr.LineTo (rFld.Right - 2, rFld.Center.Y + 0.5);
+ gr.Stroke ();
+ }
+ }
+ }
+
+ 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;
+ continue;
+ }
+ lstr = lstr.Substring (ScrollX - lPtr);
+ lPtr += ScrollX - lPtr;
+ }
+ Color bg = this.Background;
+ Color fg = this.Foreground;
+ Color selbg = this.SelectionBackground;
+ Color selfg = this.SelectionForeground;
+ FontSlant fts = FontSlant.Normal;
+ FontWeight ftw = FontWeight.Normal;
+
+ 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)
+ ftw = FontWeight.Bold;
+ if (tf.Italic)
+ fts = FontSlant.Italic;
+ }
+
+ gr.SelectFontFace (Font.Name, fts, ftw);
+ gr.SetSourceColor (fg);
+
+ gr.MoveTo (x, y + fe.Ascent);
+ gr.ShowText (lstr);
+ gr.Fill ();
+
+ 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 = y,
+ rLineW = lstr.Length * fe.MaxXAdvance;
+ double startAdjust = 0.0;
+
+ if ((lineIndex == buffer.SelectionStart.Y) && (selStartCol < lPtr + lstr.Length) && (selStartCol > lPtr))
+ startAdjust = (selStartCol - lPtr) * fe.MaxXAdvance;
+ rLineX += startAdjust;
+ if ((lineIndex == buffer.SelectionEnd.Y) && (selEndCol < lPtr + lstr.Length))
+ rLineW = (selEndCol - lPtr) * fe.MaxXAdvance;
+ rLineW -= startAdjust;
+
+ gr.Save ();
+ gr.Operator = Operator.Source;
+ gr.Rectangle (rLineX, rLineY, rLineW, fe.Height);
+ gr.SetSourceColor (selbg);
+ gr.FillPreserve ();
+ gr.Clip ();
+ gr.Operator = Operator.Over;
+ gr.SetSourceColor (selfg);
+ 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
+ public override Font Font {
+ get { return base.Font; }
+ set {
+ base.Font = value;
+
+ using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) {
+ using (Context gr = new Context (img)) {
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+
+ fe = gr.FontExtents;
+ }
+ }
+ MaxScrollY = 0;
+ RegisterForGraphicUpdate ();
+ }
+ }
+ protected override int measureRawSize(LayoutingType lt)
+ {
+ if (lt == LayoutingType.Height)
+ return (int)Math.Ceiling(fe.Height * buffer.LineCount) + Margin * 2;
+
+ return (int)(fe.MaxXAdvance * buffer.longestLineCharCount) + Margin * 2 + leftMargin;
+ }
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+
+ if (layoutType == LayoutingType.Height)
+ updateVisibleLines ();
+ else if (layoutType == LayoutingType.Width)
+ updateVisibleColumns ();
+ }
+
+ int selStartCol;
+ int selEndCol;
+
+ protected override void onDraw (Context gr)
+ {
+// if (!System.Threading.Monitor.TryEnter (buffer.EditMutex)) {
+// RegisterForGraphicUpdate ();
+// return;
+// }
+
+ base.onDraw (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
+ PrintedCurrentLine = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height));
+ int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X - leftMargin) / fe.MaxXAdvance);
+
+ 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;
+
+// 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)
+ IFace.MouseCursor = XCursor.Default;
+ else
+ IFace.MouseCursor = XCursor.Text;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ IFace.MouseCursor = XCursor.Default;
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft;
+
+ if (!e.Mouse.IsButtonDown (MouseButton.Left)) {
+ if (mouseLocalPos.X < leftMargin)
+ IFace.MouseCursor = XCursor.Default;
+ else
+ IFace.MouseCursor = XCursor.Text;
+ return;
+ }
+
+ if (!HasFocus || !buffer.SelectionInProgress)
+ return;
+
+ //mouse is down
+ updateCurrentPos();
+ buffer.SetSelEndPos ();
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ if (!this.Focusable)
+ return;
+
+ if (mouseLocalPos.X >= leftMargin)
+ base.onMouseDown (sender, e);
+
+ if (doubleClicked) {
+ doubleClicked = false;
+ return;
+ }
+
+ if (mouseLocalPos.X < leftMargin) {
+ 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)
+ {
+ base.onMouseUp (sender, e);
+
+ if (buffer.SelectionIsEmpty)
+ buffer.ResetSelection ();
+ }
+
+ public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e)
+ {
+ doubleClicked = true;
+ base.onMouseDoubleClick (sender, e);
+
+ buffer.GotoWordStart ();
+ buffer.SetSelStartPos ();
+ buffer.GotoWordEnd ();
+ buffer.SetSelEndPos ();
+ }
+
+ public override void onMouseWheel (object sender, MouseWheelEventArgs e)
+ {
+ base.onMouseWheel (sender, e);
+ }
+ #endregion
+
+ #region Keyboard handling
+ public override void onKeyDown (object sender, KeyboardKeyEventArgs e)
+ {
+ //base.onKeyDown (sender, e);
+
+ Key key = e.Key;
+
+ switch (key)
+ {
+ case Key.Back:
+ buffer.DeleteChar ();
+ break;
+ case Key.Clear:
+ break;
+ case Key.Delete:
+ if (buffer.SelectionIsEmpty)
+ MoveRight ();
+ else if (e.Shift)
+ IFace.Clipboard = buffer.SelectedText;
+ buffer.DeleteChar ();
+ break;
+ case Key.Enter:
+ case Key.KeypadEnter:
+ if (!buffer.SelectionIsEmpty)
+ buffer.DeleteChar ();
+ buffer.InsertLineBreak ();
+ break;
+ case Key.Escape:
+ buffer.ResetSelection ();
+ break;
+ case Key.Home:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (e.Control)
+ buffer.CurrentLine = 0;
+ buffer.CurrentColumn = 0;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (e.Control)
+ buffer.CurrentLine = 0;
+ buffer.CurrentColumn = 0;
+ break;
+ case Key.End:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (e.Control)
+ buffer.CurrentLine = int.MaxValue;
+ buffer.CurrentColumn = int.MaxValue;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (e.Control)
+ buffer.CurrentLine = int.MaxValue;
+ buffer.CurrentColumn = int.MaxValue;
+ break;
+ case Key.Insert:
+ if (e.Shift)
+ buffer.Insert (IFace.Clipboard);
+ else if (e.Control && !buffer.SelectionIsEmpty)
+ IFace.Clipboard = buffer.SelectedText;
+ break;
+ case Key.Left:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (e.Control)
+ buffer.GotoWordStart ();
+ else
+ MoveLeft ();
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (e.Control)
+ buffer.GotoWordStart ();
+ else
+ MoveLeft();
+ break;
+ case Key.Right:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (e.Control)
+ buffer.GotoWordEnd ();
+ else
+ MoveRight ();
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (e.Control)
+ buffer.GotoWordEnd ();
+ else
+ MoveRight ();
+ break;
+ case Key.Up:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ PrintedCurrentLine--;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ PrintedCurrentLine--;
+ break;
+ case Key.Down:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ PrintedCurrentLine++;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ PrintedCurrentLine++;
+ break;
+ case Key.Menu:
+ break;
+ case Key.NumLock:
+ break;
+ case Key.PageDown:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ PrintedCurrentLine += visibleLines;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ PrintedCurrentLine += visibleLines;
+ break;
+ case Key.PageUp:
+ if (e.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ PrintedCurrentLine -= visibleLines;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ PrintedCurrentLine -= visibleLines;
+ break;
+ case Key.RWin:
+ break;
+ case Key.Tab:
+ buffer.Insert ("\t");
+ break;
+ case Key.F8:
+ toogleFolding (buffer.CurrentLine);
+// if (parser != null)
+// reparseSource ();
+ break;
+ default:
+ break;
+ }
+ RegisterForGraphicUpdate();
+ }
+ public override void onKeyPress (object sender, KeyPressEventArgs e)
+ {
+ base.onKeyPress (sender, e);
+
+ buffer.Insert (e.KeyChar.ToString());
+ buffer.ResetSelection ();
+
+ //RegisterForGraphicUpdate();
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/CrowIDE/src/SourceEditor/TextFormatting.cs b/CrowIDE/src/SourceEditor/TextFormatting.cs
new file mode 100644
index 00000000..f7b2e518
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/TextFormatting.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Crow.Coding
+{
+ public struct TextFormatting {
+ public Color Foreground;
+ public Color Background;
+ public bool Bold;
+ public bool Italic;
+
+ public TextFormatting(Color fg, Color bg, bool bold = false, bool italic = false){
+ Foreground = fg;
+ Background = bg;
+ Bold = bold;
+ Italic = italic;
+ }
+ }
+}
+
diff --git a/CrowIDE/src/SourceEditor/Token.cs b/CrowIDE/src/SourceEditor/Token.cs
new file mode 100644
index 00000000..36f0242f
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/Token.cs
@@ -0,0 +1,70 @@
+//
+// Token.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2017 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+
+namespace Crow.Coding
+{
+ public struct Token
+ {
+ public Parser.TokenType Type;
+ public string Content;
+ 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;
+// }
+
+ public bool IsEmpty { get { return string.IsNullOrEmpty(Content); }}
+
+ public static bool operator == (Token t, System.Enum tt){
+ return Convert.ToInt32(t.Type) == Convert.ToInt32(tt);
+ }
+ public static bool operator != (Token t, System.Enum tt){
+ return Convert.ToInt32(t.Type) != Convert.ToInt32(tt);
+ }
+ public static bool operator == (System.Enum tt, Token t){
+ return Convert.ToInt32(t.Type) == Convert.ToInt32(tt);
+ }
+ public static bool operator != (System.Enum tt, Token t){
+ return Convert.ToInt32(t.Type) != Convert.ToInt32(tt);
+ }
+
+ public static Token operator +(Token t, char c){
+ t.Content += c;
+ return t;
+ }
+ public static Token operator +(Token t, string s){
+ t.Content += s;
+ return t;
+ }
+ public override string ToString ()
+ {
+ return string.Format ("[Tok{2}->{3}:{0}: {1}]", Type,Content,Start,End);
+ }
+ }
+}
+
diff --git a/CrowIDE/src/SourceEditor/XMLParser.cs b/CrowIDE/src/SourceEditor/XMLParser.cs
new file mode 100644
index 00000000..2a8ffeb1
--- /dev/null
+++ b/CrowIDE/src/SourceEditor/XMLParser.cs
@@ -0,0 +1,299 @@
+using System;
+using Crow;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+using System.Linq;
+
+namespace Crow.Coding
+{
+ public class XMLParser : Parser
+ {
+ public new enum TokenType {
+ Unknown = Parser.TokenType.Unknown,
+ 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,
+ ElementEnd,
+ ElementClosing = Parser.TokenType.StatementEnding,
+ ElementName = Parser.TokenType.Type,
+ AttributeName = Parser.TokenType.Identifier,
+ AttributeValueOpening = Parser.TokenType.StringLitteralOpening,
+ AttributeValueClosing = Parser.TokenType.StringLitteralClosing,
+ AttributeValue = Parser.TokenType.StringLitteral,
+ }
+
+ public enum States
+ {
+ init, //first statement of prolog, xmldecl should only apear in this state
+ prolog, //misc before doctypedecl
+ InternalSubset, //doctype declaration subset
+ ExternalSubsetInit,
+ ExternalSubset,
+ BlockComment,
+ DTDEnd,//doctype finished
+ XML,//normal xml
+ StartTag,//inside start tag
+ Content,//after start tag with no closing slash
+ EndTag
+ }
+
+ #region CTOR
+ public XMLParser (CodeBuffer _buffer) : base(_buffer) {}
+ #endregion
+
+ enum Keywords
+ {
+ DOCTYPE,
+ ELEMENT,
+ ATTLIST,
+ ENTITY,
+ NOTATION
+ }
+
+ States curState = States.init;
+
+ #region Regular Expression for validity checks
+ //private static Regex rxValidChar = new Regex("[\u0020-\uD7FF]");
+ private static Regex rxValidChar = new Regex(@"\u0009|\u000A|\u000D|[\u0020-\uD7FF]|[\uE000-\uFFFD]"); //| [\u10000-\u10FFFF] unable to set those plans
+ private static Regex rxNameStartChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]"); // | [\u10000-\uEFFFF]
+ private static Regex rxNameChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]");//[\u10000-\uEFFFF]|
+ private static Regex rxDecimal = new Regex(@"[0-9]+");
+ private static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+");
+ private static Regex rxAttributeValue = new Regex(@"[^<]");
+ private static Regex rxEntityValue = new Regex(@"[^<]");
+ private static Regex rxPubidChar = new Regex(@"\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-\(\)\+\,\./:=\?;!\*#@\$_%]");
+ #endregion
+
+ #region Character ValidityCheck
+ public bool nextCharIsValidCharStartName
+ {
+ get { return rxNameStartChar.IsMatch(new string(new char[]{Peek()})); }
+ }
+ public bool nextCharIsValidCharName
+ {
+ get { return rxNameChar.IsMatch(new string(new char[]{Peek()})); }
+ }
+ #endregion
+
+ public override void SetLineInError (ParsingException ex)
+ {
+ base.SetLineInError (ex);
+ //buffer[ex.Line].Tokens.EndingState = (int)States.init;
+ }
+
+ public override void ParseCurrentLine ()
+ {
+ Debug.WriteLine (string.Format("parsing line:{0}", currentLine));
+ CodeLine cl = buffer [currentLine];
+ cl.Tokens = new List<Token> ();
+
+ //retrieve current parser state from previous line
+ if (currentLine > 0)
+ curState = (States)buffer[currentLine - 1].EndingState;
+ else
+ curState = States.init;
+
+ States previousEndingState = (States)cl.EndingState;
+
+
+ while (! eol) {
+ SkipWhiteSpaces ();
+
+ if (eol)
+ break;
+
+ if (Peek () == '\n') {
+ if (currentTok != TokenType.Unknown)
+ throw new ParsingException (this, "Unexpected end of line");
+ Read ();
+ eol = true;
+ continue;
+ }
+
+ if (curState == States.BlockComment) {
+ if (currentTok != TokenType.Unknown)
+ Debugger.Break ();
+
+ currentTok.Start = CurrentPosition;
+ currentTok.Type = (Parser.TokenType)TokenType.BlockComment;
+ currentTok += ReadLineUntil ("-->");
+ if (Peek (3) == "-->") {
+ readToCurrTok (3);
+ curState = States.XML;
+ }
+ saveAndResetCurrentTok ();
+ continue;
+ }
+
+ switch (Peek()) {
+ case '<':
+ readToCurrTok (true);
+ switch (Peek()) {
+ case '?':
+ if (curState != States.init)
+ throw new ParsingException (this, "xml decl may appear only on first line");
+ readToCurrTok ();
+ currentTok += ReadLineUntil ("?>");
+ if (Peek (2) != "?>")
+ throw new ParsingException (this, "expecting '?>'");
+ readToCurrTok (2);
+ saveAndResetCurrentTok (TokenType.XMLDecl);
+ curState = States.prolog;
+ break;
+ case '!':
+ readToCurrTok ();
+ switch (Peek()) {
+ case '-':
+ readToCurrTok ();
+ if (Peek () != '-')
+ throw new ParsingException (this, "Expecting comment start tag");
+ readToCurrTok ();
+ currentTok += ReadLineUntil ("--");
+ if (Peek (3) == "-->") {
+ readToCurrTok (3);
+ }else
+ curState = States.BlockComment;
+ saveAndResetCurrentTok (TokenType.BlockComment);
+ break;
+ default:
+ throw new ParsingException(this, "error");
+ }
+ break;
+ default:
+ if (!(curState == States.Content || curState == States.XML || curState == States.init || curState == States.prolog))
+ throw new ParsingException (this, "Unexpected char: '<'");
+ if (Peek () == '/') {
+ curState = States.EndTag;
+ readToCurrTok ();
+ saveAndResetCurrentTok (TokenType.ElementEnd);
+ } else {
+ curState = States.StartTag;
+ saveAndResetCurrentTok (TokenType.ElementStart);
+ }
+
+ if (!nextCharIsValidCharStartName)
+ throw new ParsingException (this, "Expected element name");
+
+ readToCurrTok (true);
+ while (nextCharIsValidCharName)
+ readToCurrTok ();
+
+ saveAndResetCurrentTok (TokenType.ElementName);
+ break;
+ }
+ break;
+ case '/':
+ if (curState != States.StartTag)
+ throw new ParsingException (this, "Unexpected char: '/'");
+ readToCurrTok (true);
+ if (Peek () != '>')
+ throw new ParsingException (this, "Expecting '>'");
+ readAndResetCurrentTok (TokenType.ElementEnd);
+
+ curState = States.XML;
+ break;
+ case '>':
+ readAndResetCurrentTok (TokenType.ElementClosing, true);
+ switch (curState) {
+ case States.EndTag:
+ curState = States.XML;
+ break;
+ case States.StartTag:
+ curState = States.Content;
+ break;
+ default:
+ throw new ParsingException (this, "Unexpected char: '>'");
+ }
+ break;
+ default:
+ switch (curState) {
+ case States.StartTag:
+ if (!nextCharIsValidCharStartName)
+ throw new ParsingException (this, "Expected attribute name");
+ readToCurrTok (true);
+ while (nextCharIsValidCharName)
+ readToCurrTok ();
+ saveAndResetCurrentTok (TokenType.AttributeName);
+ if (Peek () != '=')
+ throw new ParsingException (this, "Expecting: '='");
+ readAndResetCurrentTok (TokenType.Affectation, true);
+
+ char openAttVal = Peek ();
+ if (openAttVal != '"' && openAttVal != '\'')
+ throw new ParsingException (this, "Expecting attribute value enclosed either in '\"' or in \"'\"");
+ readAndResetCurrentTok (TokenType.AttributeValueOpening, true);
+
+ currentTok.Start = CurrentPosition;
+ currentTok.Content = ReadLineUntil (new string (new char[]{ openAttVal }));
+ saveAndResetCurrentTok (TokenType.AttributeValue);
+
+ if (Peek () != openAttVal)
+ throw new ParsingException (this, string.Format ("Expecting {0}", openAttVal));
+ readAndResetCurrentTok (TokenType.AttributeValueClosing, true);
+ break;
+ default:
+ throw new ParsingException (this, "unexpected char: " + Peek ());
+ }
+ break;
+ }
+ }
+
+ if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1)
+ buffer [currentLine + 1].Tokens = null;
+
+ cl.EndingState = (int)curState;
+ }
+
+ public override void SyntaxAnalysis ()
+ {
+ RootNode = new Node () { Name = "RootNode", Type="Root" };
+
+ Node currentNode = RootNode;
+
+ 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 < cl.Tokens.Count) {
+ switch ((XMLParser.TokenType)cl.Tokens [tokPtr].Type) {
+ case TokenType.ElementStart:
+ tokPtr++;
+ Node newElt = new Node () { Name = cl.Tokens [tokPtr].Content, StartLine = cl };
+ currentNode.AddChild (newElt);
+ currentNode = newElt;
+ if (cl.SyntacticNode == null)
+ cl.SyntacticNode = newElt;
+ break;
+ case TokenType.ElementEnd:
+ tokPtr++;
+ 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 = cl;
+ currentNode = currentNode.Parent;
+ break;
+ case TokenType.ElementClosing:
+ //currentNode = currentNode.Parent;
+ break;
+ default:
+ break;
+ }
+ tokPtr++;
+ }
+ }
+ }
+ }
+}
+