TextEditorHighlighter.js revision cad810f21b803229eb11403f9209855525a25d57
1/* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * Copyright (C) 2009 Apple Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32WebInspector.TextEditorHighlighter = function(textModel, damageCallback) 33{ 34 this._textModel = textModel; 35 this._tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/html"); 36 this._damageCallback = damageCallback; 37 this.reset(); 38} 39 40WebInspector.TextEditorHighlighter.prototype = { 41 set mimeType(mimeType) 42 { 43 var tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer(mimeType); 44 if (tokenizer) { 45 this._tokenizer = tokenizer; 46 this._tokenizerCondition = this._tokenizer.initialCondition; 47 } 48 }, 49 50 reset: function() 51 { 52 this._lastHighlightedLine = 0; 53 this._lastHighlightedColumn = 0; 54 this._tokenizerCondition = this._tokenizer.initialCondition; 55 }, 56 57 highlight: function(endLine) 58 { 59 // First check if we have work to do. 60 if (endLine <= this._lastHighlightedLine) 61 return; 62 63 this._requestedEndLine = endLine; 64 65 if (this._highlightTimer) { 66 // There is a timer scheduled, it will catch the new job based on the new endLine set. 67 return; 68 } 69 70 // Do small highlight synchronously. This will provide instant highlight on PageUp / PageDown, gentle scrolling. 71 this._highlightInChunks(endLine); 72 73 // Schedule tail highlight if necessary. 74 if (this._lastHighlightedLine < endLine) 75 this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, endLine), 100); 76 }, 77 78 _highlightInChunks: function(endLine) 79 { 80 delete this._highlightTimer; 81 82 // First we always check if we have work to do. Could be that user scrolled back and we can quit. 83 if (this._requestedEndLine <= this._lastHighlightedLine) 84 return; 85 86 if (this._requestedEndLine !== endLine) { 87 // User keeps updating the job in between of our timer ticks. Just reschedule self, don't eat CPU (they must be scrolling). 88 this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, this._requestedEndLine), 100); 89 return; 90 } 91 92 this._highlightLines(this._requestedEndLine); 93 94 // Schedule tail highlight if necessary. 95 if (this._lastHighlightedLine < this._requestedEndLine) 96 this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, this._requestedEndLine), 10); 97 }, 98 99 _highlightLines: function(endLine) 100 { 101 // Tokenizer is stateless and reused accross viewers, restore its condition before highlight and save it after. 102 this._tokenizer.condition = this._tokenizerCondition; 103 var tokensCount = 0; 104 for (var lineNumber = this._lastHighlightedLine; lineNumber < endLine; ++lineNumber) { 105 var line = this._textModel.line(lineNumber); 106 this._tokenizer.line = line; 107 var attributes = this._textModel.getAttribute(lineNumber, "highlight") || {}; 108 109 // Highlight line. 110 do { 111 var newColumn = this._tokenizer.nextToken(this._lastHighlightedColumn); 112 var tokenType = this._tokenizer.tokenType; 113 if (tokenType) 114 attributes[this._lastHighlightedColumn] = { length: newColumn - this._lastHighlightedColumn, tokenType: tokenType, subTokenizer: this._tokenizer.subTokenizer }; 115 this._lastHighlightedColumn = newColumn; 116 if (++tokensCount > 1000) 117 break; 118 } while (this._lastHighlightedColumn < line.length) 119 120 this._textModel.setAttribute(lineNumber, "highlight", attributes); 121 if (this._lastHighlightedColumn < line.length) { 122 // Too much work for single chunk - exit. 123 break; 124 } else 125 this._lastHighlightedColumn = 0; 126 } 127 128 this._damageCallback(this._lastHighlightedLine, lineNumber); 129 this._tokenizerCondition = this._tokenizer.condition; 130 this._lastHighlightedLine = lineNumber; 131 } 132} 133