| 0: | // TextBuffer.cs | |
| 1: | // Copyright (C) 2000 Mike Krueger | |
| 2: | // Copyright (C) 2000 Andrea Paatz | |
| 3: | // | |
| 4: | // This program is free software; you can redistribute it and/or modify | |
| 5: | // it under the terms of the GNU General Public License as published by | |
| 6: | // the Free Software Foundation; either version 2 of the License, or | |
| 7: | // (at your option) any later version. | |
| 8: | // | |
| 9: | // This program is distributed in the hope that it will be useful, | |
| 10: | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11: | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12: | // GNU General Public License for more details. | |
| 13: | // | |
| 14: | // You should have received a copy of the GNU General Public License | |
| 15: | // along with this program; if not, write to the Free Software | |
| 16: | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 17: | ||
| 18: | using System; | |
| 19: | using System.Drawing; | |
| 20: | using System.Diagnostics; | |
| 21: | using System.Collections; | |
| 22: | using System.IO; | |
| 23: | using System.Text; | |
| 24: | using System.Windows.Forms; | |
| 25: | ||
| 26: | using SharpDevelop.Tool.Data; | |
| 27: | using SharpDevelop.Internal.Undo; | |
| 28: | using SharpDevelop.Gui.Edit; // for the ProgressEventHandler | |
| 29: | ||
| 30: | namespace SharpDevelop.Internal.Text { | |
| 31: | ||
| 32: | public class Fold | |
| 33: | { | |
| 34: | public int Start; | |
| 35: | public int Length; | |
| 36: | ||
| 37: | public Fold(int start, int length) | |
| 38: | { | |
| 39: | this.Start = start; | |
| 40: | this.Length = length; | |
| 41: | } | |
| 42: | } | |
| 43: | ||
| 44: | public class Foldings | |
| 45: | { | |
| 46: | TextBuffer buffer; | |
| 47: | public SortedList foldings = new SortedList(); | |
| 48: | ||
| 49: | public Foldings(TextBuffer buffer) | |
| 50: | { | |
| 51: | this.buffer = buffer; | |
| 52: | buffer.MoveIndices += new MoveIndicesHandler(MoveIndices); | |
| 53: | } | |
| 54: | ||
| 55: | public void MoveIndices(int index, int count) | |
| 56: | { | |
| 57: | SortedList newfoldings = new SortedList(); | |
| 58: | bool redraw = false; | |
| 59: | foreach (DictionaryEntry folding in foldings) { | |
| 60: | int foldat = (int)folding.Key; | |
| 61: | Console.WriteLine("index " + index + " count " + count + " foldat " + foldat); | |
| 62: | if (count < 0 && foldat >= index + count && foldat < index) {// delete cleared folding; | |
| 63: | Console.WriteLine("ShouldRedraw"); | |
| 64: | redraw = true; | |
| 65: | continue; | |
| 66: | } | |
| 67: | if (foldat >= index) { | |
| 68: | foldat += count; | |
| 69: | } | |
| 70: | newfoldings[foldat] = folding.Value; | |
| 71: | } | |
| 72: | buffer.UpdateRequested |= redraw; | |
| 73: | foldings = newfoldings; | |
| 74: | } | |
| 75: | ||
| 76: | public int GetPhysicalLine(int linenumber) | |
| 77: | { | |
| 78: | int maxline = 0; | |
| 79: | foreach (DictionaryEntry folding in foldings) { | |
| 80: | if (folding.Value == null) | |
| 81: | continue; | |
| 82: | int foldat = (int)folding.Key; | |
| 83: | int length = (int)folding.Value; | |
| 84: | if (foldat <= linenumber && maxline <= foldat + length) { | |
| 85: | maxline = foldat + length; | |
| 86: | linenumber += length; | |
| 87: | } else | |
| 88: | break; | |
| 89: | } | |
| 90: | return linenumber; | |
| 91: | } | |
| 92: | ||
| 93: | public int GetLogicalLine(int linenumber) | |
| 94: | { | |
| 95: | int folded = 0; | |
| 96: | int maxline = 0; | |
| 97: | foreach (DictionaryEntry folding in foldings) { | |
| 98: | if (folding.Value == null) | |
| 99: | continue; | |
| 100: | int foldat = (int)folding.Key; | |
| 101: | int length = (int)folding.Value; | |
| 102: | if (foldat <= linenumber && maxline <= foldat + length) { | |
| 103: | maxline = foldat + length; | |
| 104: | folded += length; | |
| 105: | } else | |
| 106: | break; | |
| 107: | } | |
| 108: | return linenumber - folded; | |
| 109: | } | |
| 110: | ||
| 111: | public void FoldPos(Point start) | |
| 112: | { | |
| 113: | int y1 = -1; | |
| 114: | int y2 = -1; | |
| 115: | ||
| 116: | int foldlevel = 1; | |
| 117: | for (int i = start.Y; i >= 0; --i) { | |
| 118: | if (buffer[i].foldoff) | |
| 119: | ++foldlevel; | |
| 120: | if (buffer[i].foldon) { | |
| 121: | --foldlevel; | |
| 122: | if (foldlevel <= 0) { | |
| 123: | y1 = i; | |
| 124: | break; | |
| 125: | } | |
| 126: | } | |
| 127: | } | |
| 128: | ||
| 129: | foldlevel = 1; | |
| 130: | if (buffer[start.Y].foldon) | |
| 131: | foldlevel--; | |
| 132: | for (int i = start.Y; i < buffer.Length; ++i) { | |
| 133: | if (buffer[i].foldon) | |
| 134: | ++foldlevel; | |
| 135: | if (buffer[i].foldoff) { | |
| 136: | --foldlevel; | |
| 137: | if (foldlevel <= 0) { | |
| 138: | y2 = i; | |
| 139: | break; | |
| 140: | } | |
| 141: | } | |
| 142: | } | |
| 143: | if (y1 == -1 || y2 == -1) | |
| 144: | return; | |
| 145: | buffer.Foldings.Fold(y1 + 1, y2 - y1 - 1); | |
| 146: | } | |
| 147: | ||
| 148: | public void Fold(int start, int length) | |
| 149: | { | |
| 150: | if (foldings[start] == null) { | |
| 151: | foldings[start] = length; | |
| 152: | for (int i = 0; i < length; ++i) { | |
| 153: | buffer[start + i].Visible = false; | |
| 154: | } | |
| 155: | } | |
| 156: | } | |
| 157: | ||
| 158: | public void UnFold(int start) | |
| 159: | { | |
| 160: | if (foldings[start] != null) { | |
| 161: | int length = (int)foldings[start]; | |
| 162: | buffer[start].Visible = true; | |
| 163: | for (int i = 1; i < length; ++i) { | |
| 164: | if (foldings[start + i] != null) // skip inner folding | |
| 165: | i += (int)foldings[start + i]; | |
| 166: | buffer[start + i].Visible = true; | |
| 167: | } | |
| 168: | foldings[start] = null; | |
| 169: | } | |
| 170: | } | |
| 171: | public void UnFoldAll() | |
| 172: | { | |
| 173: | foldings.Clear(); | |
| 174: | for (int i = 0; i < buffer.Length; ++i) | |
| 175: | buffer[i].Visible = true; | |
| 176: | } | |
| 177: | } | |
| 178: | ||
| 179: | public delegate void MoveIndicesHandler(int index, int count); | |
| 180: | ||
| 181: | /// <summary> | |
| 182: | /// This class represents a Text. The text is in the container MyArrayList line | |
| 183: | /// it is divided up in TextLines and TextLines provide the string as a line or | |
| 184: | /// as divided up into words, which contain syntax highlighting information. | |
| 185: | /// </summary> | |
| 186: | public class TextBuffer | |
| 187: | { | |
| 188: | Syntax syntax = new Syntax(); | |
| 189: | UndoStack undostack = null; | |
| 190: | ||
| 191: | MyArrayList line = null; | |
| 192: | ||
| 193: | Bookmark bookmark; | |
| 194: | bool updaterequested = false; | |
| 195: | bool onlyreadable = false; | |
| 196: | bool updated = false; | |
| 197: | ||
| 198: | BufferOptions options = (BufferOptions)((ICloneable)Options.GetProperty("SharpDevelop.Internal.Text.TextBuffer.BufferOptions", new BufferOptions())).Clone(); | |
| 199: | ||
| 200: | public Foldings Foldings = null; | |
| 201: | ||
| 202: | public BufferOptions BufferOptions { | |
| 203: | get { | |
| 204: | return options; | |
| 205: | } | |
| 206: | } | |
| 207: | ||
| 208: | public event MoveIndicesHandler MoveIndices; | |
| 209: | public event ProgressEventHandler Progress; | |
| 210: | public event EventHandler Changed; | |
| 211: | public event EventHandler FoldChanged; | |
| 212: | public event EventHandler LineCountChanged; | |
| 213: | ||
| 214: | public UndoStack UndoStack { | |
| 215: | get { | |
| 216: | return undostack; | |
| 217: | } | |
| 218: | } | |
| 219: | public bool ReadOnly { | |
| 220: | get { | |
| 221: | return onlyreadable; | |
| 222: | } | |
| 223: | set { | |
| 224: | onlyreadable = value; | |
| 225: | } | |
| 226: | } | |
| 227: | ||
| 228: | public Syntax Syntax { | |
| 229: | get { | |
| 230: | return syntax; | |
| 231: | } | |
| 232: | } | |
| 233: | ||
| 234: | public Bookmark Bookmark { | |
| 235: | get { | |
| 236: | return bookmark; | |
| 237: | } | |
| 238: | } | |
| 239: | ||
| 240: | public bool UpdateRequested { | |
| 241: | get { | |
| 242: | return updaterequested; | |
| 243: | } | |
| 244: | set { | |
| 245: | updaterequested= value; | |
| 246: | } | |
| 247: | } | |
| 248: | ||
| 249: | public string Text { | |
| 250: | get { | |
| 251: | string back = ""; | |
| 252: | for (int i = 0; i < line.Count - 1; ++i) | |
| 253: | back += ((TextLine)line[i]).Text + "\n"; | |
| 254: | back += ((TextLine)line[line.Count - 1]).Text; | |
| 255: | return back; | |
| 256: | } | |
| 257: | set { | |
| 258: | line = new MyArrayList(this); | |
| 259: | string[] lines = value.Split(new char[] {'\n'}); | |
| 260: | for (int i = 0; i < lines.Length; ++i) | |
| 261: | line.Add(new TextLine(this, i, lines[i])); | |
| 262: | } | |
| 263: | } | |
| 264: | ||
| 265: | public ArrayList Line { | |
| 266: | get { | |
| 267: | return line; | |
| 268: | } | |
| 269: | } | |
| 270: | ||
| 271: | public int Length { | |
| 272: | get { | |
| 273: | return line.Count; | |
| 274: | } | |
| 275: | } | |
| 276: | ||
| 277: | public TextLine this[int i] { | |
| 278: | get { | |
| 279: | if (line.Count == 0) | |
| 280: | throw new ArgumentException("TextBuffer public TextLine this[int i] : Buffer is empty"); | |
| 281: | return (TextLine)line[i]; | |
| 282: | } | |
| 283: | } | |
| 284: | ||
| 285: | public TextBuffer() | |
| 286: | { | |
| 287: | undostack = new UndoStack(); | |
| 288: | bookmark = new Bookmark(this); | |
| 289: | Foldings = new Foldings(this); | |
| 290: | line = new MyArrayList(this); | |
| 291: | line.Add(new TextLine(this, 0, "")); | |
| 292: | ||
| 293: | SetDefaultHighlighting(); | |
| 294: | ||
| 295: | OnChanged(); | |
| 296: | } | |
| 297: | ||
| 298: | void Reparse() | |
| 299: | { | |
| 300: | for (int i = 0; i < this.Length; ++i) | |
| 301: | this[i].SetText(i, this[i].Text); | |
| 302: | } | |
| 303: | ||
| 304: | public void BeginUpdate() | |
| 305: | { | |
| 306: | updated = true; | |
| 307: | } | |
| 308: | public void EndUpdate() | |
| 309: | { | |
| 310: | updated = false; | |
| 311: | } | |
| 312: | ||
| 313: | protected virtual void OnChanged() | |
| 314: | { | |
| 315: | if (!updated && Changed != null) { | |
| 316: | Changed(this, null); | |
| 317: | } | |
| 318: | } | |
| 319: | ||
| 320: | public void OnMoveIndices(int index, int count) | |
| 321: | { | |
| 322: | if (MoveIndices != null) | |
| 323: | MoveIndices(index, count); | |
| 324: | } | |
| 325: | ||
| 326: | void OnLineCountChanged() | |
| 327: | { | |
| 328: | if (LineCountChanged != null) { | |
| 329: | LineCountChanged(this, null); | |
| 330: | } | |
| 331: | } | |
| 332: | ||
| 333: | protected virtual void OnFoldEvent() | |
| 334: | { | |
| 335: | if (!updated && FoldChanged != null) | |
| 336: | FoldChanged(this, null); | |
| 337: | } | |
| 338: | ||
| 339: | public void Load(string filename) | |
| 340: | { | |
| 341: | line = new MyArrayList(this); | |
| 342: | StreamReader stream = File.OpenText(filename); | |
| 343: | int linenumber = 0; | |
| 344: | int lastpercent = -1; | |
| 345: | while (true) { | |
| 346: | string curLine = stream.ReadLine(); | |
| 347: | int percent = (int)((stream.BaseStream.Position * 100) / (stream.BaseStream.Length + 1)); | |
| 348: | ||
| 349: | if (Progress != null && percent != lastpercent) { | |
| 350: | Progress(this, percent); | |
| 351: | lastpercent = percent; | |
| 352: | } | |
| 353: | ||
| 354: | if (curLine == null) { | |
| 355: | if (linenumber == 0) | |
| 356: | line.Add(new TextLine(this, ++linenumber, "")); | |
| 357: | break; | |
| 358: | } | |
| 359: | ||
| 360: | line.Add(new TextLine(this, ++linenumber, curLine)); | |
| 361: | } | |
| 362: | if (line.Count == 0) // file empty, insert a blank line | |
| 363: | line.Add(new TextLine(this, ++linenumber, "")); | |
| 364: | stream.Close(); | |
| 365: | OnChanged(); | |
| 366: | } | |
| 367: | ||
| 368: | public void SetDefaultHighlighting() | |
| 369: | { | |
| 370: | SetHighlightingTo("Default"); | |
| 371: | Reparse(); | |
| 372: | } | |
| 373: | ||
| 374: | public void SetHighlightingFor(string filename) | |
| 375: | { | |
| 376: | string extension = Path.GetExtension(filename).ToUpper(); | |
| 377: | foreach (Syntax syn in Syntax.SyntaxDefinitions) { | |
| 378: |