Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/IndentEngine/CacheIndentEngine.cs @ 15943

Last change on this file since 15943 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 15.9 KB
Line 
1//
2// CacheIndentEngine.cs
3//
4// Author:
5//       Matej Miklečić <matej.miklecic@gmail.com>
6//
7// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26using ICSharpCode.NRefactory.Editor;
27using System;
28using System.Collections.Generic;
29using System.Linq;
30
31namespace ICSharpCode.NRefactory.CSharp
32{
33  /// <summary>
34  ///     Represents a decorator of an IStateMachineIndentEngine instance that provides
35  ///     logic for reseting and updating the engine on text changed events.
36  /// </summary>
37  /// <remarks>
38  ///     The decorator is based on periodical caching of the engine's state and
39  ///     delegating all logic behind indentation to the currently active engine.
40  /// </remarks>
41  public class CacheIndentEngine : IStateMachineIndentEngine
42  {
43
44    #region Properties
45
46    IStateMachineIndentEngine currentEngine;
47    Stack<IStateMachineIndentEngine> cachedEngines = new Stack<IStateMachineIndentEngine>();
48
49    #endregion
50
51    #region Constructors
52
53    /// <summary>
54    ///     Creates a new CacheIndentEngine instance.
55    /// </summary>
56    /// <param name="decoratedEngine">
57    ///     An instance of <see cref="IStateMachineIndentEngine"/> to which the
58    ///     logic for indentation will be delegated.
59    /// </param>
60    /// <param name="cacheRate">
61    ///     The number of chars between caching.
62    /// </param>
63    public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000)
64    {
65      this.currentEngine = decoratedEngine;
66    }
67
68    /// <summary>
69    ///     Creates a new CacheIndentEngine instance from the given prototype.
70    /// </summary>
71    /// <param name="prototype">
72    ///     A CacheIndentEngine instance.
73    /// </param>
74    public CacheIndentEngine(CacheIndentEngine prototype)
75    {
76      this.currentEngine = prototype.currentEngine.Clone();
77    }
78
79    #endregion
80
81    #region IDocumentIndentEngine
82
83    /// <inheritdoc />
84    public IDocument Document {
85      get { return currentEngine.Document; }
86    }
87
88    /// <inheritdoc />
89    public string ThisLineIndent {
90      get { return currentEngine.ThisLineIndent; }
91    }
92
93    /// <inheritdoc />
94    public string NextLineIndent {
95      get { return currentEngine.NextLineIndent; }
96    }
97
98    /// <inheritdoc />
99    public string CurrentIndent {
100      get { return currentEngine.CurrentIndent; }
101    }
102
103    /// <inheritdoc />
104    public bool NeedsReindent {
105      get { return currentEngine.NeedsReindent; }
106    }
107
108    /// <inheritdoc />
109    public int Offset {
110      get { return currentEngine.Offset; }
111    }
112
113    /// <inheritdoc />
114    public TextLocation Location {
115      get { return currentEngine.Location; }
116    }
117
118    /// <inheritdoc />
119    public bool EnableCustomIndentLevels
120    {
121      get { return currentEngine.EnableCustomIndentLevels; }
122      set { currentEngine.EnableCustomIndentLevels = value; }
123    }
124
125    /// <inheritdoc />
126    public void Push(char ch)
127    {
128      currentEngine.Push(ch);
129    }
130
131    /// <inheritdoc />
132    public void Reset()
133    {
134      currentEngine.Reset();
135      cachedEngines.Clear();
136    }
137
138    /// <summary>
139    /// Resets the engine to offset. Clears all cached engines after the given offset.
140    /// </summary>
141    public void ResetEngineToPosition(int offset)
142    {
143      // We are already there
144      if (currentEngine.Offset <= offset)
145        return;
146     
147      bool gotCachedEngine = false;
148      while (cachedEngines.Count > 0) {
149        var topEngine = cachedEngines.Peek();
150        if (topEngine.Offset <= offset) {
151          currentEngine = topEngine.Clone();
152          gotCachedEngine = true;
153          break;
154        } else {
155          cachedEngines.Pop();
156        }
157      }
158      if (!gotCachedEngine)
159        currentEngine.Reset();
160    }
161
162    /// <inheritdoc />
163    /// <remarks>
164    ///     If the <paramref name="position"/> is negative, the engine will
165    ///     update to: document.TextLength + (offset % document.TextLength+1)
166    ///     Otherwise it will update to: offset % document.TextLength+1
167    /// </remarks>
168    public void Update(int position)
169    {
170      const int BUFFER_SIZE = 2000;
171     
172      if (currentEngine.Offset == position) {
173        //positions match, nothing to be done
174        return;
175      } else if (currentEngine.Offset > position) {
176        //moving backwards, so reset from previous saved location
177        ResetEngineToPosition(position);
178      }
179
180      // get the engine caught up
181      int nextSave = (cachedEngines.Count == 0) ? BUFFER_SIZE : cachedEngines.Peek().Offset + BUFFER_SIZE;
182      if (currentEngine.Offset + 1 == position) {
183        char ch = currentEngine.Document.GetCharAt(currentEngine.Offset);
184        currentEngine.Push(ch);
185        if (currentEngine.Offset == nextSave)
186          cachedEngines.Push(currentEngine.Clone());
187      } else {
188        //bulk copy characters in case buffer is unmanaged
189        //(faster if we reduce managed/unmanaged transitions)
190        while (currentEngine.Offset < position) {
191          int endCut = currentEngine.Offset + BUFFER_SIZE;
192          if (endCut > position)
193            endCut = position;
194          string buffer = currentEngine.Document.GetText(currentEngine.Offset, endCut - currentEngine.Offset);
195          foreach (char ch in buffer) {
196            currentEngine.Push(ch);
197            //ConsoleWrite ("pushing character '{0}'", ch);
198            if (currentEngine.Offset == nextSave) {
199              cachedEngines.Push(currentEngine.Clone());
200              nextSave += BUFFER_SIZE;
201            }
202          }
203        }
204      }
205    }
206
207    public IStateMachineIndentEngine GetEngine(int offset)
208    {
209      ResetEngineToPosition(offset);
210      return currentEngine;
211    }
212
213    #endregion
214
215    #region IClonable
216
217    /// <inheritdoc />
218    public IStateMachineIndentEngine Clone()
219    {
220      return new CacheIndentEngine(this);
221    }
222
223    /// <inheritdoc />
224    IDocumentIndentEngine IDocumentIndentEngine.Clone()
225    {
226      return Clone();
227    }
228
229    object ICloneable.Clone()
230    {
231      return Clone();
232    }
233
234    #endregion
235
236    #region IStateMachineIndentEngine
237
238    public bool IsInsidePreprocessorDirective {
239      get { return currentEngine.IsInsidePreprocessorDirective; }
240    }
241
242    public bool IsInsidePreprocessorComment {
243      get { return currentEngine.IsInsidePreprocessorComment; }
244    }
245
246    public bool IsInsideStringLiteral {
247      get { return currentEngine.IsInsideStringLiteral; }
248    }
249
250    public bool IsInsideVerbatimString {
251      get { return currentEngine.IsInsideVerbatimString; }
252    }
253
254    public bool IsInsideCharacter {
255      get { return currentEngine.IsInsideCharacter; }
256    }
257
258    public bool IsInsideString {
259      get { return currentEngine.IsInsideString; }
260    }
261
262    public bool IsInsideLineComment {
263      get { return currentEngine.IsInsideLineComment; }
264    }
265
266    public bool IsInsideMultiLineComment {
267      get { return currentEngine.IsInsideMultiLineComment; }
268    }
269
270    public bool IsInsideDocLineComment {
271      get { return currentEngine.IsInsideDocLineComment; }
272    }
273
274    public bool IsInsideComment {
275      get { return currentEngine.IsInsideComment; }
276    }
277
278    public bool IsInsideOrdinaryComment {
279      get { return currentEngine.IsInsideOrdinaryComment; }
280    }
281
282    public bool IsInsideOrdinaryCommentOrString {
283      get { return currentEngine.IsInsideOrdinaryCommentOrString; }
284    }
285
286    public bool LineBeganInsideVerbatimString {
287      get { return currentEngine.LineBeganInsideVerbatimString; }
288    }
289
290    public bool LineBeganInsideMultiLineComment {
291      get { return currentEngine.LineBeganInsideMultiLineComment; }
292    }
293
294    #endregion
295
296  }
297  /*
298/   // <summary>
299  ///     Represents a decorator of an IStateMachineIndentEngine instance that provides
300  ///     logic for reseting and updating the engine on text changed events.
301  /// </summary>
302  /// <remarks>
303  ///     The decorator is based on periodical caching of the engine's state and
304  ///     delegating all logic behind indentation to the currently active engine.
305  /// </remarks>
306  public class CacheIndentEngine : IStateMachineIndentEngine
307  {
308    #region Properties
309   
310    /// <summary>
311    ///     Represents the cache interval in number of chars pushed to the engine.
312    /// </summary>
313    /// <remarks>
314    ///     When this many new chars are pushed to the engine, the currently active
315    ///     engine gets cloned and added to the end of <see cref="cachedEngines"/>.
316    /// </remarks>
317    readonly int cacheRate;
318   
319    /// <summary>
320    ///     Determines how much memory to reserve on initialization for the
321    ///     cached engines.
322    /// </summary>
323    const int cacheCapacity = 25;
324   
325    /// <summary>
326    ///     Currently active engine.
327    /// </summary>
328    /// <remarks>
329    ///     Should be equal to the last engine in <see cref="cachedEngines"/>.
330    /// </remarks>
331    IStateMachineIndentEngine currentEngine;
332   
333    /// <summary>
334    ///     List of cached engines sorted ascending by
335    ///     <see cref="IDocumentIndentEngine.Offset"/>.
336    /// </summary>
337    IStateMachineIndentEngine[] cachedEngines;
338   
339    /// <summary>
340    ///     The index of the last cached engine in cachedEngines.
341    /// </summary>
342    /// <remarks>
343    ///     Should be equal to: currentEngine.Offset / CacheRate
344    /// </remarks>
345    int lastCachedEngine;
346   
347    #endregion
348   
349    #region Constructors
350   
351    /// <summary>
352    ///     Creates a new CacheIndentEngine instance.
353    /// </summary>
354    /// <param name="decoratedEngine">
355    ///     An instance of <see cref="IStateMachineIndentEngine"/> to which the
356    ///     logic for indentation will be delegated.
357    /// </param>
358    /// <param name="cacheRate">
359    ///     The number of chars between caching.
360    /// </param>
361    public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000)
362    {
363      this.cachedEngines = new IStateMachineIndentEngine[cacheCapacity];
364     
365      this.cachedEngines[0] = decoratedEngine.Clone();
366      this.currentEngine = this.cachedEngines[0].Clone();
367      this.cacheRate = cacheRate;
368    }
369   
370    /// <summary>
371    ///     Creates a new CacheIndentEngine instance from the given prototype.
372    /// </summary>
373    /// <param name="prototype">
374    ///     A CacheIndentEngine instance.
375    /// </param>
376    public CacheIndentEngine(CacheIndentEngine prototype)
377    {
378      this.cachedEngines = new IStateMachineIndentEngine[prototype.cachedEngines.Length];
379      Array.Copy(prototype.cachedEngines, this.cachedEngines, prototype.cachedEngines.Length);
380     
381      this.lastCachedEngine = prototype.lastCachedEngine;
382      this.currentEngine = prototype.currentEngine.Clone();
383      this.cacheRate = prototype.cacheRate;
384    }
385   
386    #endregion
387   
388    #region Methods
389   
390    /// <summary>
391    ///     Performs caching of the <see cref="CacheIndentEngine.currentEngine"/>.
392    /// </summary>
393    void cache()
394    {
395      if (currentEngine.Offset % cacheRate != 0)
396      {
397        throw new Exception("The current engine's offset is not divisable with the cacheRate.");
398      }
399     
400      // determine the new current engine from cachedEngines
401      lastCachedEngine = currentEngine.Offset / cacheRate;
402     
403      if (cachedEngines.Length < lastCachedEngine + 1)
404      {
405        Array.Resize(ref cachedEngines, lastCachedEngine * 2);
406      }
407     
408      cachedEngines[lastCachedEngine] = currentEngine.Clone();
409    }
410   
411    #endregion
412   
413    #region IDocumentIndentEngine
414   
415    /// <inheritdoc />
416    public IDocument Document
417    {
418      get { return currentEngine.Document; }
419    }
420   
421    /// <inheritdoc />
422    public string ThisLineIndent
423    {
424      get { return currentEngine.ThisLineIndent; }
425    }
426   
427    /// <inheritdoc />
428    public string NextLineIndent
429    {
430      get { return currentEngine.NextLineIndent; }
431    }
432   
433    /// <inheritdoc />
434    public string CurrentIndent
435    {
436      get { return currentEngine.CurrentIndent; }
437    }
438   
439    /// <inheritdoc />
440    public bool NeedsReindent
441    {
442      get { return currentEngine.NeedsReindent; }
443    }
444   
445    /// <inheritdoc />
446    public int Offset
447    {
448      get { return currentEngine.Offset; }
449    }
450   
451    /// <inheritdoc />
452    public TextLocation Location
453    {
454      get { return currentEngine.Location; }
455    }
456   
457    /// <inheritdoc />
458    public void Push(char ch)
459    {
460      currentEngine.Push(ch);
461     
462      if (currentEngine.Offset % cacheRate == 0)
463      {
464        cache();
465      }
466    }
467   
468    /// <inheritdoc />
469    public void Reset()
470    {
471      currentEngine = cachedEngines[lastCachedEngine = 0];
472    }
473   
474    /// <inheritdoc />
475    /// <remarks>
476    ///     If the <paramref name="offset"/> is negative, the engine will
477    ///     update to: document.TextLength + (offset % document.TextLength+1)
478    ///     Otherwise it will update to: offset % document.TextLength+1
479    /// </remarks>
480    public void Update(int offset)
481    {
482      // map the given offset to the [0, document.TextLength] interval
483      // using modulo arithmetics
484      offset %= Document.TextLength + 1;
485      if (offset < 0)
486      {
487        offset += Document.TextLength + 1;
488      }
489     
490      // check if the engine has to be updated to some previous offset
491      if (currentEngine.Offset > offset)
492      {
493        // replace the currentEngine with the first one whose offset
494        // is less then the given <paramref name="offset"/>
495        lastCachedEngine =  offset / cacheRate;
496        currentEngine = cachedEngines[lastCachedEngine].Clone();
497      }
498     
499      // update the engine to the given offset
500      while (Offset < offset)
501      {
502        Push(Document.GetCharAt(Offset));
503      }
504    }
505   
506    public IStateMachineIndentEngine GetEngine(int offset)
507    {
508      // map the given offset to the [0, document.TextLength] interval
509      // using modulo arithmetics
510      offset %= Document.TextLength + 1;
511      if (offset < 0)
512      {
513        offset += Document.TextLength + 1;
514      }
515     
516      // check if the engine has to be updated to some previous offset
517      if (currentEngine.Offset > offset)
518      {
519        // replace the currentEngine with the first one whose offset
520        // is less then the given <paramref name="offset"/>
521        lastCachedEngine =  offset / cacheRate;
522        return cachedEngines[lastCachedEngine].Clone();
523      }
524     
525      return currentEngine;
526    }
527   
528    #endregion
529   
530    #region IClonable
531   
532    /// <inheritdoc />
533    public IStateMachineIndentEngine Clone()
534    {
535      return new CacheIndentEngine(this);
536    }
537   
538    /// <inheritdoc />
539    IDocumentIndentEngine IDocumentIndentEngine.Clone()
540    {
541      return Clone();
542    }
543   
544    object ICloneable.Clone()
545    {
546      return Clone();
547    }
548   
549    #endregion
550   
551    #region IStateMachineIndentEngine
552   
553    public bool IsInsidePreprocessorDirective
554    {
555      get { return currentEngine.IsInsidePreprocessorDirective; }
556    }
557   
558    public bool IsInsidePreprocessorComment
559    {
560      get { return currentEngine.IsInsidePreprocessorComment; }
561    }
562   
563    public bool IsInsideStringLiteral
564    {
565      get { return currentEngine.IsInsideStringLiteral; }
566    }
567   
568    public bool IsInsideVerbatimString
569    {
570      get { return currentEngine.IsInsideVerbatimString; }
571    }
572   
573    public bool IsInsideCharacter
574    {
575      get { return currentEngine.IsInsideCharacter; }
576    }
577   
578    public bool IsInsideString
579    {
580      get { return currentEngine.IsInsideString; }
581    }
582   
583    public bool IsInsideLineComment
584    {
585      get { return currentEngine.IsInsideLineComment; }
586    }
587   
588    public bool IsInsideMultiLineComment
589    {
590      get { return currentEngine.IsInsideMultiLineComment; }
591    }
592   
593    public bool IsInsideDocLineComment
594    {
595      get { return currentEngine.IsInsideDocLineComment; }
596    }
597   
598    public bool IsInsideComment
599    {
600      get { return currentEngine.IsInsideComment; }
601    }
602   
603    public bool IsInsideOrdinaryComment
604    {
605      get { return currentEngine.IsInsideOrdinaryComment; }
606    }
607   
608    public bool IsInsideOrdinaryCommentOrString
609    {
610      get { return currentEngine.IsInsideOrdinaryCommentOrString; }
611    }
612   
613    public bool LineBeganInsideVerbatimString
614    {
615      get { return currentEngine.LineBeganInsideVerbatimString; }
616    }
617   
618    public bool LineBeganInsideMultiLineComment
619    {
620      get { return currentEngine.LineBeganInsideMultiLineComment; }
621    }
622   
623    #endregion
624  }
625 
626  */
627}
Note: See TracBrowser for help on using the repository browser.