OSDN Git Service

GapBufferが更新されていた
[fooeditengine/FooEditEngine.git] / Core / LineToIndex.cs
index 1bf4fd8..605bc02 100644 (file)
@@ -9,8 +9,8 @@
 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.Text.RegularExpressions;
-using System.Threading;
+using System.Text;
+using System.Globalization;
 using System.Linq;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -76,6 +76,23 @@ namespace FooEditEngine
         double GetColPostionFromIndex(int index);
 
         /// <summary>
+        /// 座標に対応するインデックスを取得する
+        /// </summary>
+        /// <param name="x">桁方向の座標</param>
+        /// <param name="y">行方向の座標</param>
+        /// <returns>インデックス</returns>
+        /// <remarks>行番号の幅は考慮されてないのでView以外のクラスは呼び出さないでください</remarks>
+        int GetIndexFromPostion(double x, double y);
+
+        /// <summary>
+        /// インデックスに対応する座標を得る
+        /// </summary>
+        /// <param name="index">インデックス</param>
+        /// <returns>行方向と桁方向の相対座標</returns>
+        /// <remarks>行頭にEOFが含まれている場合、0が返ります</remarks>
+        Point GetPostionFromIndex(int index);
+
+        /// <summary>
         /// 適切な位置にインデックスを調整する
         /// </summary>
         /// <param name="index">インデックス</param>
@@ -121,6 +138,13 @@ namespace FooEditEngine
         End,
     }
 
+    interface ILineInfoGenerator
+    {
+        void Update(Document doc, int startIndex, int insertLength, int removeLength);
+        void Clear(LineToIndexTable lti);
+        bool Generate(Document doc, LineToIndexTable lti, bool force = true);
+    }
+
     internal class LineToIndexTableData : IDisposable
     {
         /// <summary>
@@ -209,22 +233,23 @@ namespace FooEditEngine
     public sealed class LineToIndexTable : IEnumerable<string>
     {
         const int MaxEntries = 100;
-        Queue<ITextLayout> CacheEntries = new Queue<ITextLayout>();
         GapBuffer<LineToIndexTableData> Lines = new GapBuffer<LineToIndexTableData>();
         Document Document;
-        long lastUpdateTicks = DateTime.Now.Ticks;
-        const long AllowCallTicks = 1000 * 10000;   //see.DateTime.Ticks プロパティ
-        bool _IsSync;
         ITextRender render;
         int stepRow = -1,stepLength = 0;
         const int STEP_ROW_IS_NONE = -1;
 
+        const int FOLDING_INDEX = 0;
+        const int SYNTAX_HIGLITHER_INDEX = 1;
+        ILineInfoGenerator[] _generators = new ILineInfoGenerator[2];
+
         internal LineToIndexTable(Document buf)
         {
             this.Document = buf;
             this.Document.Markers.Updated += Markers_Updated;
-            this.FoldingCollection = new FoldingCollection();
-            this._IsSync = true;
+            this._generators[FOLDING_INDEX] = new FoldingGenerator();
+            this._generators[SYNTAX_HIGLITHER_INDEX] = new SyntaxHilightGenerator();
+            this.WrapWidth = NONE_BREAK_LINE;
 #if DEBUG && !NETFX_CORE
             if (!Debugger.IsAttached)
             {
@@ -268,27 +293,88 @@ namespace FooEditEngine
         /// </summary>
         public FoldingCollection FoldingCollection
         {
-            get;
-            private set;
+            get
+            {
+                return ((FoldingGenerator)this._generators[FOLDING_INDEX]).FoldingCollection;
+            }
+            private set
+            {
+            }
         }
 
         /// <summary>
         /// シンタックスハイライター
         /// </summary>
-        internal IHilighter Hilighter { get; set; }
+        public IHilighter Hilighter
+        {
+            get
+            {
+                return ((SyntaxHilightGenerator)this._generators[SYNTAX_HIGLITHER_INDEX]).Hilighter;
+            }
+            set
+            {
+                ((SyntaxHilightGenerator)this._generators[SYNTAX_HIGLITHER_INDEX]).Hilighter = value;
+                if (value == null)
+                    this._generators[FOLDING_INDEX].Clear(this);
+            }
+        }
+
+        /// <summary>
+        /// 折り畳み
+        /// </summary>
+        public IFoldingStrategy FoldingStrategy
+        {
+            get
+            {
+                return ((FoldingGenerator)this._generators[FOLDING_INDEX]).FoldingStrategy;
+            }
+            set
+            {
+                ((FoldingGenerator)this._generators[FOLDING_INDEX]).FoldingStrategy = value;
+                if (value == null)
+                    this._generators[FOLDING_INDEX].Clear(this);
+            }
+        }
 
-        internal IFoldingStrategy FoldingStrategy { get; set; }
+        /// <summary>
+        /// ピクセル単位で折り返すかどうか
+        /// </summary>
+        public double WrapWidth
+        {
+            get;
+            set;
+        }
+
+        /// <summary>
+        /// 行を折り返さないことを表す
+        /// </summary>
+        public const double NONE_BREAK_LINE = -1;
 
         /// <summary>
         /// 保持しているレイアウトキャッシュをクリアーする
         /// </summary>
         public void ClearLayoutCache()
         {
-            foreach (ITextLayout data in this.CacheEntries)
+            foreach (LineToIndexTableData data in this.Lines)
             {
                 data.Dispose();
             }
-            this.CacheEntries.Clear();
+        }
+
+        /// <summary>
+        /// 保持しているレイアウトキャッシュをクリアーする
+        /// </summary>
+        public void ClearLayoutCache(int index,int length)
+        {
+            if (index >= this.Document.Length)
+                return;
+            int startRow = this.GetLineNumberFromIndex(index);
+            int lastIndex = Math.Min(index + length - 1, this.Document.Length - 1);
+            if (lastIndex < 0)
+                lastIndex = 0;
+            int endRow = this.GetLineNumberFromIndex(lastIndex);
+            for (int i = startRow; i <= endRow; i++)
+                this.Lines[i].Dispose();
         }
 
         /// <summary>
@@ -326,12 +412,6 @@ namespace FooEditEngine
                 return this.Lines[row].Index;
         }
 
-        internal LineToIndexTableData CreateLineToIndexTableData(int index, int length, bool lineend, SyntaxInfo[] syntax)
-        {
-            LineToIndexTableData result = new LineToIndexTableData(index, length, lineend,this.IsFrozneDirtyFlag == false, syntax);
-            return result;
-        }
-
         internal void UpdateLineAsReplace(int row,int removedLength, int insertedLength)
         {
             int deltaLength = insertedLength - removedLength;
@@ -341,11 +421,8 @@ namespace FooEditEngine
             //行テーブルを更新する
             this.UpdateLineHeadIndex(deltaLength, row, 1);
 
-            this.FoldingCollection.UpdateData(this.Document, this.GetLineHeadIndex(row), insertedLength, removedLength);
-
-            this._IsSync = false;
-
-            this.lastUpdateTicks = DateTime.Now.Ticks;
+            foreach(var generator in this._generators)
+                generator.Update(this.Document, this.GetLineHeadIndex(row), insertedLength, removedLength);
         }
 
         internal void UpdateAsReplace(int index, int removedLength, int insertedLength)
@@ -364,7 +441,7 @@ namespace FooEditEngine
 
             //挿入範囲内のドキュメントから行を生成する
             SpilitStringEventArgs e = new SpilitStringEventArgs(this.Document, HeadIndex, analyzeLength, startRow);
-            IList<LineToIndexTableData> newLines = SpilitString(this, e);
+            IList<LineToIndexTableData> newLines = this.CreateLineList(e.index, e.length, Document.MaximumLineLength);
 
             //消すべき行が複数ある場合は消すが、そうでない場合は最適化のため長さを変えるだけにとどめておく
             int removeCount = endRow - startRow + 1;
@@ -385,11 +462,58 @@ namespace FooEditEngine
 
             this.AddDummyLine();
 
-            this.FoldingCollection.UpdateData(this.Document, index, insertedLength, removedLength);            
+            foreach (var generator in this._generators)
+                generator.Update(this.Document, index, insertedLength, removedLength);
+        }
 
-            this._IsSync = false;
+        internal IEnumerable<Tuple<int, int>> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)
+        {
+            int currentLineHeadIndex = startIndex;
+            int currentLineLength = 0;
 
-            this.lastUpdateTicks = DateTime.Now.Ticks;
+            for (int i = startIndex; i <= endIndex; i++)
+            {
+                currentLineLength++;
+                char c = this.Document[i];
+                if (c == Document.NewLine ||
+                    (maxCharCount != -1 && currentLineLength >= maxCharCount))
+                {
+                    UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);
+                    if (uc != UnicodeCategory.NonSpacingMark &&
+                    uc != UnicodeCategory.SpacingCombiningMark &&
+                    uc != UnicodeCategory.EnclosingMark &&
+                    uc != UnicodeCategory.Surrogate)
+                    {
+                        yield return new Tuple<int, int>(currentLineHeadIndex, currentLineLength);
+                        currentLineHeadIndex += currentLineLength;
+                        currentLineLength = 0;
+                    }
+                }
+            }
+            if (currentLineLength > 0)
+                yield return new Tuple<int, int>(currentLineHeadIndex, currentLineLength);
+        }
+
+        IList<LineToIndexTableData> CreateLineList(int index, int length, int lineLimitLength = -1)
+        {
+            int startIndex = index;
+            int endIndex = index + length - 1;
+            List<LineToIndexTableData> output = new List<LineToIndexTableData>();
+
+            foreach (Tuple<int, int> range in this.ForEachLines(startIndex, endIndex, lineLimitLength))
+            {
+                int lineHeadIndex = range.Item1;
+                int lineLength = range.Item2;
+                char c = this.Document[lineHeadIndex + lineLength - 1];
+                bool hasNewLine = c == Document.NewLine;
+                LineToIndexTableData result = new LineToIndexTableData(lineHeadIndex, lineLength, hasNewLine, this.IsFrozneDirtyFlag == false, null);
+                output.Add(result);
+            }
+
+            if (output.Count > 0)
+                output.Last().LineEnd = true;
+
+            return output;
         }
 
         void GetRemoveRange(int index,int length,out int startRow,out int endRow)
@@ -537,6 +661,17 @@ namespace FooEditEngine
         }
 
         /// <summary>
+        /// 生データを取得します
+        /// </summary>
+        /// <param name="row">行</param>
+        /// <returns>LineToIndexTableData</returns>
+        /// <remarks>いくつかの値は実態とかけ離れた値を返します。詳しくはLineToIndexTableDataの注意事項を参照すること</remarks>
+        internal LineToIndexTableData GetRaw(int row)
+        {
+            return this.Lines[row];
+        }
+
+        /// <summary>
         /// 行番号をインデックスに変換します
         /// </summary>
         /// <param name="row">行番号</param>
@@ -572,6 +707,16 @@ namespace FooEditEngine
             return this.Lines[row].Dirty;
         }
 
+        /// <summary>
+        /// 行の高さを返す
+        /// </summary>
+        /// <param name="tp">テキストポイント</param>
+        /// <returns>テキストポイントで指定された行の高さを返します</returns>
+        public double GetLineHeight(TextPoint tp)
+        {
+            return this.render.emSize.Height;
+        }
+
         internal ITextLayout GetLayout(int row)
         {
             if (this.Lines[row].Layout != null && this.Lines[row].Layout.Invaild)
@@ -592,7 +737,7 @@ namespace FooEditEngine
             LineToIndexTableData lineData = this.Lines[row];
             if (lineData.Length == 0)
             {
-                layout = this.render.CreateLaytout("", null, null);
+                layout = this.render.CreateLaytout("", null, null, null,this.WrapWidth);
             }
             else
             {
@@ -602,20 +747,21 @@ namespace FooEditEngine
 
                 if (this.CreateingLayout != null)
                     this.CreateingLayout(this, new CreateLayoutEventArgs(lineHeadIndex, lineData.Length,content));
-                
-                var markerRange = from id in this.Document.Markers.IDs
+
+                var userMarkerRange = from id in this.Document.Markers.IDs
                                   from s in this.Document.Markers.Get(id,lineHeadIndex,lineData.Length)
                                   let n = Util.ConvertAbsIndexToRelIndex(s, lineHeadIndex, lineData.Length)
                                   select n;
-                layout = this.render.CreateLaytout(content, lineData.Syntax, markerRange);
-            }
+                var watchdogMarkerRange = from s in this.Document.MarkerPatternSet.GetMarkers(new CreateLayoutEventArgs(lineHeadIndex, lineData.Length, content))
+                                          let n = Util.ConvertAbsIndexToRelIndex(s, lineHeadIndex, lineData.Length)
+                                          select n;
+                var markerRange = watchdogMarkerRange.Concat(userMarkerRange);
+                var selectRange = from s in this.Document.Selections.Get(lineHeadIndex, lineData.Length)
+                                  let n = Util.ConvertAbsIndexToRelIndex(s, lineHeadIndex, lineData.Length)
+                                  select n;
 
-            if (this.CacheEntries.Count > MaxEntries)
-            {
-                ITextLayout oldItem = this.CacheEntries.Dequeue();
-                oldItem.Dispose();
+                layout = this.render.CreateLaytout(content, lineData.Syntax, markerRange, selectRange,this.WrapWidth);
             }
-            this.CacheEntries.Enqueue(layout);
 
             return layout;
         }
@@ -716,42 +862,7 @@ namespace FooEditEngine
         /// <remarks>デフォルトではドキュメントが更新されている時にだけ再生成されます</remarks>
         public bool GenerateFolding(bool force = false)
         {
-            if (this.Document.Length == 0 || this.Document.IsLocked)
-                return false;
-            long nowTick = DateTime.Now.Ticks;
-            bool sync = force || !this._IsSync;
-            if (sync || Math.Abs(nowTick - this.lastUpdateTicks) >= AllowCallTicks)
-            {
-                this.GenerateFolding(0, this.Document.Length - 1);
-                this.lastUpdateTicks = nowTick;
-                return true;
-            }
-            return false;
-        }
-
-        void GenerateFolding(int start, int end)
-        {
-            if (start > end)
-                throw new ArgumentException("start <= endである必要があります");
-            if (this.FoldingStrategy != null)
-            {
-                //再生成するとすべて展開状態になってしまうので、閉じてるやつだけを保存しておく
-                FoldingItem[] closed_items =  this.FoldingCollection.Where((e)=> { return !e.Expand; }).ToArray();
-
-                this.FoldingCollection.Clear();
-
-                var items = this.FoldingStrategy.AnalyzeDocument(this.Document, start, end)
-                    .Where((item) =>
-                    {
-                        int startRow = this.GetLineNumberFromIndex(item.Start);
-                        int endRow = this.GetLineNumberFromIndex(item.End);
-                        return startRow != endRow;
-                    })
-                    .Select((item) => item);
-                this.FoldingCollection.AddRange(items);
-
-                this.FoldingCollection.ApplyExpandStatus(closed_items);
-            }
+            return this._generators[FOLDING_INDEX].Generate(this.Document, this, force);
         }
 
         /// <summary>
@@ -759,8 +870,7 @@ namespace FooEditEngine
         /// </summary>
         public void ClearFolding()
         {
-            this.FoldingCollection.Clear();
-            this._IsSync = false;
+            this._generators[FOLDING_INDEX].Clear(this);
         }
 
         /// <summary>
@@ -768,24 +878,7 @@ namespace FooEditEngine
         /// </summary>
         public bool HilightAll(bool force = false)
         {
-            if (this.Hilighter == null || this.Document.IsLocked)
-                return false;
-
-            long nowTick = DateTime.Now.Ticks;
-            bool sync = force || !this._IsSync;
-            if (sync || Math.Abs(nowTick - this.lastUpdateTicks) >= AllowCallTicks)
-            {
-                for (int i = 0; i < this.Lines.Count; i++)
-                    this.HilightLine(i);
-
-                this.Hilighter.Reset();
-                this.ClearLayoutCache();
-
-                this.lastUpdateTicks = nowTick;
-
-                return true;
-            }
-            return false;
+            return this._generators[SYNTAX_HIGLITHER_INDEX].Generate(this.Document, this, force);
         }
 
         /// <summary>
@@ -793,9 +886,7 @@ namespace FooEditEngine
         /// </summary>
         public void ClearHilight()
         {
-            foreach (LineToIndexTableData line in this.Lines)
-                line.Syntax = null;
-            this.ClearLayoutCache();
+            this._generators[SYNTAX_HIGLITHER_INDEX].Clear(this);
         }
 
         /// <summary>
@@ -804,7 +895,7 @@ namespace FooEditEngine
         internal void Clear()
         {
             this.ClearLayoutCache();
-            this.FoldingCollection.Clear();
+            this.ClearFolding();
             this.Lines.Clear();
             LineToIndexTableData dummy = new LineToIndexTableData();
             this.Lines.Add(dummy);
@@ -813,25 +904,6 @@ namespace FooEditEngine
             Debug.WriteLine("Clear");
         }
 
-        private void HilightLine(int row)
-        {
-            //シンタックスハイライトを行う
-            List<SyntaxInfo> syntax = new List<SyntaxInfo>();
-            string str = this[row];
-            int level = this.Hilighter.DoHilight(str, str.Length, (s) =>
-            {
-                if (s.type == TokenType.None || s.type == TokenType.Control)
-                    return;
-                if (str[s.index + s.length - 1] == Document.NewLine)
-                    s.length--;
-                syntax.Add(new SyntaxInfo(s.index, s.length, s.type));
-            });
-
-            LineToIndexTableData lineData = this.Lines[row];
-            lineData.Syntax = syntax.ToArray();
-
-        }
-
         #region IEnumerable<string> メンバー
 
         /// <summary>