1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * Copyright (C) 2010 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 32importScript("ace/ace.js"); 33 34 35/** 36 * @constructor 37 * @extends {WebInspector.View} 38 * @implements {WebInspector.TextEditor} 39 * @param {?string} url 40 * @param {WebInspector.TextEditorDelegate} delegate 41 */ 42 43WebInspector.AceTextEditor = function(url, delegate) 44{ 45 WebInspector.View.call(this); 46 this._delegate = delegate; 47 this._url = url; 48 this.element.className = "ace-editor-container source-code"; 49 50 var prefix = window.flattenImports ? "" : "ace/"; 51 ace.config.setModuleUrl("ace/mode/javascript", prefix + "mode_javascript.js"); 52 ace.config.setModuleUrl("ace/mode/javascript", prefix + "mode_css.js"); 53 ace.config.setModuleUrl("ace/mode/javascript", prefix + "mode_html.js"); 54 ace.config.setModuleUrl("ace/theme/textmate", prefix + "theme_textmate.js"); 55 this._aceEditor = window.ace.edit(this.element); 56 57 this._aceEditor.setShowFoldWidgets(false); 58 this._aceEditor.session.setFixedGutterWidth(true); 59 this._aceEditor.on("gutterclick", this._gutterClick.bind(this)); 60 this._aceEditor.on("change", this._onTextChange.bind(this)); 61 this._aceEditor.setHighlightActiveLine(false); 62 this._aceEditor.session.setUseWorker(false); 63 this.registerRequiredCSS("ace/acedevtools.css"); 64 this._attributes = []; 65} 66 67WebInspector.AceTextEditor.prototype = { 68 69 _updateBreakpoints: function() 70 { 71 this._aceEditor.session.clearBreakpoints(); 72 for(var i in this._attributes) { 73 var breakpoint = this.getAttribute(i, "ace_breakpoint"); 74 if (!breakpoint) 75 continue; 76 var className = breakpoint.conditional ? "webkit-breakpoint-conditional" : "webkit-breakpoint"; 77 if (breakpoint.disabled) className += " webkit-breakpoint-disabled"; 78 this._aceEditor.session.setBreakpoint(i, className); 79 } 80 }, 81 82 _updateLineAttributes: function(delta) { 83 var range = delta.range; 84 var length, insertionIndex; 85 86 if (range.end.row === range.start.row) 87 return; 88 89 if (delta.action === "insertText") { 90 length = range.end.row - range.start.row; 91 insertionIndex = range.start.column === 0 ? range.start.row: range.start.row + 1; 92 } else if (delta.action === "insertLines") { 93 length = range.end.row - range.start.row; 94 insertionIndex = range.start.row; 95 } else if (delta.action === "removeText") { 96 length = range.start.row - range.end.row; 97 insertionIndex = range.start.row; 98 } else if (delta.action === "removeLines") { 99 length = range.start.row - range.end.row; 100 insertionIndex = range.start.row; 101 } 102 103 if (length > 0) { 104 var spliceArguments = new Array(length); 105 spliceArguments.unshift(insertionIndex, 0); 106 Array.prototype.splice.apply(this._attributes, spliceArguments); 107 } else if (length < 0) { 108 this._attributes.splice(insertionIndex, -length); 109 } 110 this._updateBreakpoints(); 111 }, 112 113 _onTextChange: function(event) 114 { 115 this._updateLineAttributes(event.data); 116 this._delegate.onTextChanged(null, null); 117 }, 118 119 _gutterClick: function(event) 120 { 121 var lineNumber = parseInt(event.domEvent.target.textContent) - 1; 122 this.dispatchEventToListeners(WebInspector.TextEditor.Events.GutterClick, { lineNumber: lineNumber, event: event.domEvent }); 123 }, 124 125 /** 126 * @param {string} mimeType 127 */ 128 set mimeType(mimeType) 129 { 130 switch(mimeType) { 131 case "text/html": 132 this._aceEditor.getSession().setMode("ace/mode/html"); 133 break; 134 case "text/css": 135 this._aceEditor.getSession().setMode("ace/mode/css"); 136 break; 137 case "text/javascript": 138 this._aceEditor.getSession().setMode("ace/mode/javascript"); 139 break; 140 } 141 }, 142 143 /** 144 * @param {boolean} readOnly 145 */ 146 setReadOnly: function(readOnly) 147 { 148 this._aceEditor.setReadOnly(readOnly); 149 }, 150 151 /** 152 * @return {boolean} 153 */ 154 readOnly: function() 155 { 156 return this._aceEditor.getReadOnly(); 157 }, 158 159 focus: function() 160 { 161 this._aceEditor.focus(); 162 }, 163 164 /** 165 * @return {Element} 166 */ 167 defaultFocusedElement: function() 168 { 169 return this.element.firstChild; 170 }, 171 172 /** 173 * @param {string} regex 174 * @param {string} cssClass 175 * @return {WebInspector.TextEditorMainPanel.HighlightDescriptor} 176 */ 177 highlightRegex: function(regex, cssClass) 178 { 179 console.log("aceEditor.highlightRegex not implemented"); 180 }, 181 182 /** 183 * @param {WebInspector.TextRange} range 184 * @param {string} cssClass 185 */ 186 highlightRange: function(range, cssClass) 187 { 188 console.log("aceEditor.highlightRange not implemented"); 189 }, 190 191 /** 192 * @param {WebInspector.TextEditorMainPanel.HighlightDescriptor} highlightDescriptor 193 */ 194 removeHighlight: function(highlightDescriptor) 195 { 196 console.log("aceEditor.removeHighlight not implemented"); 197 }, 198 199 /** 200 * @param {number} lineNumber 201 */ 202 revealLine: function(lineNumber) { 203 this._aceEditor.scrollToLine(lineNumber, false, true); 204 }, 205 206 /** 207 * @param {number} lineNumber 208 * @param {boolean} disabled 209 * @param {boolean} conditional 210 */ 211 addBreakpoint: function(lineNumber, disabled, conditional) 212 { 213 this.setAttribute(lineNumber, "ace_breakpoint", { 214 disabled: disabled, 215 conditional: conditional 216 }); 217 this._updateBreakpoints(); 218 }, 219 220 /** 221 * @param {number} lineNumber 222 */ 223 removeBreakpoint: function(lineNumber) 224 { 225 this.removeAttribute(lineNumber, "ace_breakpoint"); 226 this._updateBreakpoints(); 227 }, 228 229 /** 230 * @param {number} lineNumber 231 */ 232 setExecutionLine: function(lineNumber) 233 { 234 this._executionLine = lineNumber; 235 const Range = ace.require('ace/range').Range; 236 this._executionLineMarker = this._aceEditor.session.addMarker(new Range(lineNumber, 0, lineNumber, Infinity), "webkit-execution-line", "fullLine"); 237 this._aceEditor.session.addGutterDecoration(lineNumber, "webkit-gutter-execution-line"); 238 }, 239 240 /** 241 * @param {WebInspector.TextRange} range 242 * @return {string} 243 */ 244 copyRange: function(range) 245 { 246 console.log("aceEditor.copyRange not implemented"); 247 return ""; 248 }, 249 250 clearExecutionLine: function() 251 { 252 this._aceEditor.session.removeMarker(this._executionLineMarker); 253 this._aceEditor.session.removeGutterDecoration(this._executionLine, "webkit-gutter-execution-line"); 254 }, 255 256 /** 257 * @param {number} lineNumber 258 * @param {Element} element 259 */ 260 addDecoration: function(lineNumber, element) 261 { 262 console.log("aceEditor.addDecoration not implemented"); 263 }, 264 265 /** 266 * @param {number} lineNumber 267 * @param {Element} element 268 */ 269 removeDecoration: function(lineNumber, element) 270 { 271 console.log("aceEditor.removeDecoration not implemented"); 272 }, 273 274 /** 275 * @param {WebInspector.TextRange} range 276 */ 277 markAndRevealRange: function(range) 278 { 279 console.log("aceEditor.markAndRevealRange not implemented"); 280 }, 281 282 /** 283 * @param {number} lineNumber 284 */ 285 highlightLine: function(lineNumber) 286 { 287 console.log("aceEditor.highlightLine not implemented"); 288 }, 289 290 clearLineHighlight: function() { 291 console.log("aceEditor.clearLineHighlight not implemented"); 292 }, 293 294 /** 295 * @return {Array.<Element>} 296 */ 297 elementsToRestoreScrollPositionsFor: function() 298 { 299 return []; 300 }, 301 302 /** 303 * @param {WebInspector.TextEditor} textEditor 304 */ 305 inheritScrollPositions: function(textEditor) 306 { 307 console.log("aceEditor.inheritScrollPositions not implemented"); 308 }, 309 310 beginUpdates: function() { }, 311 312 endUpdates: function() { }, 313 314 onResize: function() { }, 315 316 /** 317 * @param {WebInspector.TextRange} range 318 * @param {string} text 319 * @return {WebInspector.TextRange} 320 */ 321 editRange: function(range, text) 322 { 323 console.log("aceEditor.editRange not implemented"); 324 }, 325 326 /** 327 * @param {number} lineNumber 328 */ 329 scrollToLine: function(lineNumber) 330 { 331 this._aceEditor.scrollToLine(lineNumber, false, true); 332 }, 333 334 /** 335 * @return {WebInspector.TextRange} 336 */ 337 selection: function() 338 { 339 console.log("aceEditor.selection not implemented"); 340 }, 341 342 /** 343 * @return {WebInspector.TextRange?} 344 */ 345 lastSelection: function() 346 { 347 console.log("aceEditor.lastSelection not implemented"); 348 }, 349 350 /** 351 * @param {WebInspector.TextRange} textRange 352 */ 353 setSelection: function(textRange) 354 { 355 this._aceEditor.scrollToLine(textRange.startLine, true); 356 }, 357 358 /** 359 * @param {string} text 360 */ 361 setText: function(text) 362 { 363 this._aceEditor.getSession().setValue(text); 364 }, 365 366 /** 367 * @return {string} 368 */ 369 text: function() 370 { 371 return this._aceEditor.getSession().getValue(); 372 }, 373 374 /** 375 * @return {WebInspector.TextRange} 376 */ 377 range: function() 378 { 379 console.log("aceEditor.range not implemented"); 380 }, 381 382 /** 383 * @param {number} lineNumber 384 * @return {string} 385 */ 386 line: function(lineNumber) 387 { 388 return this._aceEditor.getSession().getLine(lineNumber); 389 }, 390 391 /** 392 * @return {number} 393 */ 394 get linesCount() { 395 return this._aceEditor.getSession().getLength(); 396 }, 397 398 /** 399 * @param {number} line 400 * @param {string} name 401 * @return {Object|null} value 402 */ 403 getAttribute: function(line, name) 404 { 405 var attrs = this._attributes[line]; 406 return attrs ? attrs[name] : null; 407 }, 408 409 /** 410 * @param {number} line 411 * @param {string} name 412 * @param {Object?} value 413 */ 414 setAttribute: function(line, name, value) 415 { 416 var attrs = this._attributes[line]; 417 if (!attrs) { 418 attrs = {}; 419 this._attributes[line] = attrs; 420 } 421 attrs[name] = value; 422 }, 423 424 /** 425 * @param {number} line 426 * @param {string} name 427 */ 428 removeAttribute: function(line, name) 429 { 430 var attrs = this._attributes[line]; 431 if (attrs) 432 delete attrs[name]; 433 }, 434 435 wasShown: function() { }, 436 437 __proto__: WebInspector.View.prototype 438} 439