diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/TextEditor.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/TextEditor.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/TextEditor.js | 1326 |
1 files changed, 0 insertions, 1326 deletions
diff --git a/Source/WebInspectorUI/UserInterface/TextEditor.js b/Source/WebInspectorUI/UserInterface/TextEditor.js deleted file mode 100644 index 0e92eb108..000000000 --- a/Source/WebInspectorUI/UserInterface/TextEditor.js +++ /dev/null @@ -1,1326 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -WebInspector.TextEditor = function(element, mimeType, delegate) -{ - WebInspector.Object.call(this); - - var text = (element ? element.textContent : ""); - this._element = element || document.createElement("div"); - this._element.classList.add(WebInspector.TextEditor.StyleClassName); - this._element.classList.add(WebInspector.SyntaxHighlightedStyleClassName); - - this._codeMirror = CodeMirror(this.element, { - readOnly: true, - indentWithTabs: true, - indentUnit: 4, - lineNumbers: true, - lineWrapping: true, - matchBrackets: true, - autoCloseBrackets: true - }); - - this._codeMirror.on("change", this._contentChanged.bind(this)); - this._codeMirror.on("gutterClick", this._gutterMouseDown.bind(this)); - this._codeMirror.on("gutterContextMenu", this._gutterContextMenu.bind(this)); - this._codeMirror.getScrollerElement().addEventListener("click", this._openClickedLinks.bind(this), true); - - this._completionController = new WebInspector.CodeMirrorCompletionController(this._codeMirror, this); - this._tokenTrackingController = new WebInspector.CodeMirrorTokenTrackingController(this._codeMirror, this); - - this._initialStringNotSet = true; - - this.mimeType = mimeType; - - this._breakpoints = {}; - this._executionLineNumber = NaN; - this._executionColumnNumber = NaN; - - this._searchQuery = null; - this._searchResults = []; - this._currentSearchResultIndex = -1; - this._ignoreCodeMirrorContentDidChangeEvent = 0; - - this._formatted = false; - this._formatterSourceMap = null; - - this._delegate = delegate || null; -}; - -WebInspector.Object.addConstructorFunctions(WebInspector.TextEditor); - -WebInspector.TextEditor.StyleClassName = "text-editor"; -WebInspector.TextEditor.HighlightedStyleClassName = "highlighted"; -WebInspector.TextEditor.SearchResultStyleClassName = "search-result"; -WebInspector.TextEditor.HasBreakpointStyleClassName = "has-breakpoint"; -WebInspector.TextEditor.BreakpointResolvedStyleClassName = "breakpoint-resolved"; -WebInspector.TextEditor.BreakpointAutoContinueStyleClassName = "breakpoint-auto-continue"; -WebInspector.TextEditor.BreakpointDisabledStyleClassName = "breakpoint-disabled"; -WebInspector.TextEditor.MultipleBreakpointsStyleClassName = "multiple-breakpoints"; -WebInspector.TextEditor.ExecutionLineStyleClassName = "execution-line"; -WebInspector.TextEditor.BouncyHighlightStyleClassName = "bouncy-highlight"; -WebInspector.TextEditor.NumberOfFindsPerSearchBatch = 10; -WebInspector.TextEditor.HighlightAnimationDuration = 2000; - -WebInspector.TextEditor.Event = { - ExecutionLineNumberDidChange: "text-editor-execution-line-number-did-change", - NumberOfSearchResultsDidChange: "text-editor-number-of-search-results-did-change", - ContentDidChange: "text-editor-content-did-change", - FormattingDidChange: "text-editor-formatting-did-change" -}; - -WebInspector.TextEditor.prototype = { - constructor: WebInspector.TextEditor, - - // Public - - get element() - { - return this._element; - }, - - get string() - { - return this._codeMirror.getValue(); - }, - - set string(newString) - { - function update() - { - this._codeMirror.setValue(newString); - - if (this._initialStringNotSet) { - this._codeMirror.clearHistory(); - this._codeMirror.markClean(); - delete this._initialStringNotSet; - } - - // Automatically format the content. - if (this._autoFormat) { - console.assert(!this.formatted); - this.formatted = true; - delete this._autoFormat; - } - - // Update the execution line now that we might have content for that line. - this._updateExecutionLine(); - - // Set the breakpoint styles now that we might have content for those lines. - for (var lineNumber in this._breakpoints) - this._setBreakpointStylesOnLine(lineNumber); - - // Try revealing the pending line now that we might have content with enough lines. - this._revealPendingPositionIfPossible(); - } - - this._ignoreCodeMirrorContentDidChangeEvent++; - this._codeMirror.operation(update.bind(this)); - this._ignoreCodeMirrorContentDidChangeEvent--; - console.assert(this._ignoreCodeMirrorContentDidChangeEvent >= 0); - }, - - get readOnly() - { - return this._codeMirror.getOption("readOnly") || false; - }, - - set readOnly(readOnly) - { - this._codeMirror.setOption("readOnly", readOnly); - }, - - get formatted() - { - return this._formatted; - }, - - set formatted(formatted) - { - if (this._formatted === formatted) - return; - - console.assert(!formatted || this.canBeFormatted()); - if (formatted && !this.canBeFormatted()) - return; - - this._ignoreCodeMirrorContentDidChangeEvent++; - this._prettyPrint(formatted); - this._ignoreCodeMirrorContentDidChangeEvent--; - console.assert(this._ignoreCodeMirrorContentDidChangeEvent >= 0); - - this._formatted = formatted; - - this.dispatchEventToListeners(WebInspector.TextEditor.Event.FormattingDidChange); - }, - - set autoFormat(auto) - { - this._autoFormat = auto; - }, - - hasFormatter: function() - { - const supportedModes = { - "javascript": true, - "css": true, - }; - - var mode = this._codeMirror.getMode(); - return mode.name in supportedModes; - }, - - canBeFormatted: function() - { - // Can be overriden by subclasses. - return this.hasFormatter(); - }, - - get selectedTextRange() - { - var start = this._codeMirror.getCursor(true); - var end = this._codeMirror.getCursor(false); - return this._textRangeFromCodeMirrorPosition(start, end); - }, - - set selectedTextRange(textRange) - { - var position = this._codeMirrorPositionFromTextRange(textRange); - this._codeMirror.setSelection(position.start, position.end); - }, - - get mimeType() - { - return this._mimeType; - }, - - set mimeType(newMIMEType) - { - newMIMEType = parseMIMEType(newMIMEType).type; - - this._mimeType = newMIMEType; - this._codeMirror.setOption("mode", newMIMEType); - }, - - get executionLineNumber() - { - return this._executionLineNumber; - }, - - set executionLineNumber(lineNumber) - { - // Only return early if there isn't a line handle and that isn't changing. - if (!this._executionLineHandle && isNaN(lineNumber)) - return; - - this._executionLineNumber = lineNumber; - this._updateExecutionLine(); - - // Still dispatch the event even if the number didn't change. The execution state still - // could have changed (e.g. continuing in a loop with a breakpoint inside). - this.dispatchEventToListeners(WebInspector.TextEditor.Event.ExecutionLineNumberDidChange); - }, - - get executionColumnNumber() - { - return this._executionColumnNumber; - }, - - set executionColumnNumber(columnNumber) - { - this._executionColumnNumber = columnNumber; - }, - - get formatterSourceMap() - { - return this._formatterSourceMap; - }, - - get tokenTrackingController() - { - return this._tokenTrackingController; - }, - - get delegate() - { - return this._delegate; - }, - - set delegate(newDelegate) - { - this._delegate = newDelegate || null; - }, - - get numberOfSearchResults() - { - return this._searchResults.length; - }, - - get currentSearchQuery() - { - return this._searchQuery; - }, - - set automaticallyRevealFirstSearchResult(reveal) - { - this._automaticallyRevealFirstSearchResult = reveal; - - // If we haven't shown a search result yet, reveal one now. - if (this._automaticallyRevealFirstSearchResult && this._searchResults.length > 0) { - if (this._currentSearchResultIndex === -1) - this._revealFirstSearchResultAfterCursor(); - } - }, - - performSearch: function(query) - { - if (this._searchQuery === query) - return; - - this.searchCleared(); - - this._searchQuery = query; - - // Allow subclasses to handle the searching if they have a better way. - // If we are formatted, just use CodeMirror's search. - if (typeof this.customPerformSearch === "function" && !this.formatted) { - if (this.customPerformSearch(query)) - return; - } - - // Go down the slow patch for all other text content. - var searchCursor = this._codeMirror.getSearchCursor(query, {line: 0, ch: 0}, true); - var boundBatchSearch = batchSearch.bind(this); - var numberOfSearchResultsDidChangeTimeout = null; - - function reportNumberOfSearchResultsDidChange() - { - if (numberOfSearchResultsDidChangeTimeout) { - clearTimeout(numberOfSearchResultsDidChangeTimeout); - numberOfSearchResultsDidChangeTimeout = null; - } - - this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange); - } - - function batchSearch() - { - // Bail if the query changed since we started. - if (this._searchQuery !== query) - return; - - var newSearchResults = []; - var foundResult = false; - for (var i = 0; i < WebInspector.TextEditor.NumberOfFindsPerSearchBatch && (foundResult = searchCursor.findNext()); ++i) { - var textRange = this._textRangeFromCodeMirrorPosition(searchCursor.from(), searchCursor.to()); - newSearchResults.push(textRange); - } - - this.addSearchResults(newSearchResults); - - // Don't report immediately, coalesce updates so they come in no faster than half a second. - if (!numberOfSearchResultsDidChangeTimeout) - numberOfSearchResultsDidChangeTimeout = setTimeout(reportNumberOfSearchResultsDidChange.bind(this), 500); - - if (foundResult) { - // More lines to search, set a timeout so we don't block the UI long. - setTimeout(boundBatchSearch, 50); - } else { - // Report immediately now that we are finished, canceling any pending update. - reportNumberOfSearchResultsDidChange.call(this); - } - } - - // Start the search. - boundBatchSearch(); - }, - - addSearchResults: function(textRanges) - { - console.assert(textRanges); - if (!textRanges || !textRanges.length) - return; - - function markRanges() - { - for (var i = 0; i < textRanges.length; ++i) { - var position = this._codeMirrorPositionFromTextRange(textRanges[i]); - var mark = this._codeMirror.markText(position.start, position.end, {className: WebInspector.TextEditor.SearchResultStyleClassName}); - this._searchResults.push(mark); - } - - // If we haven't shown a search result yet, reveal one now. - if (this._automaticallyRevealFirstSearchResult) { - if (this._currentSearchResultIndex === -1) - this._revealFirstSearchResultAfterCursor(); - } - } - - this._codeMirror.operation(markRanges.bind(this)); - }, - - searchCleared: function() - { - function clearResults() { - for (var i = 0; i < this._searchResults.length; ++i) - this._searchResults[i].clear(); - } - - this._codeMirror.operation(clearResults.bind(this)); - - this._searchQuery = null; - this._searchResults = []; - this._currentSearchResultIndex = -1; - }, - - searchQueryWithSelection: function() - { - if (!this._codeMirror.somethingSelected()) - return null; - - return this._codeMirror.getSelection(); - }, - - revealPreviousSearchResult: function(changeFocus) - { - if (!this._searchResults.length) - return; - - if (this._currentSearchResultIndex === -1 || this._cursorDoesNotMatchLastRevealedSearchResult()) { - this._revealFirstSearchResultBeforeCursor(changeFocus); - return; - } - - if (this._currentSearchResultIndex > 0) - --this._currentSearchResultIndex; - else - this._currentSearchResultIndex = this._searchResults.length - 1; - - this._revealSearchResult(this._searchResults[this._currentSearchResultIndex], changeFocus, -1); - }, - - revealNextSearchResult: function(changeFocus) - { - if (!this._searchResults.length) - return; - - if (this._currentSearchResultIndex === -1 || this._cursorDoesNotMatchLastRevealedSearchResult()) { - this._revealFirstSearchResultAfterCursor(changeFocus); - return; - } - - if (this._currentSearchResultIndex + 1 < this._searchResults.length) - ++this._currentSearchResultIndex; - else - this._currentSearchResultIndex = 0; - - this._revealSearchResult(this._searchResults[this._currentSearchResultIndex], changeFocus, 1); - }, - - line: function(lineNumber) - { - return this._codeMirror.getLine(lineNumber); - }, - - revealPosition: function(position, textRangeToSelect, forceUnformatted, noHighlight) - { - console.assert(position === undefined || position instanceof WebInspector.SourceCodePosition, "revealPosition called without a SourceCodePosition"); - if (!(position instanceof WebInspector.SourceCodePosition)) - return; - - var lineHandle = this._codeMirror.getLineHandle(position.lineNumber); - if (!lineHandle || !this._visible || this._initialStringNotSet) { - // If we can't get a line handle or are not visible then we wait to do the reveal. - this._positionToReveal = position; - this._textRangeToSelect = textRangeToSelect; - this._forceUnformatted = forceUnformatted; - return; - } - - // Delete now that the reveal is happening. - delete this._positionToReveal; - delete this._textRangeToSelect; - delete this._forceUnformatted; - - // If we need to unformat, reveal the line after a wait. - // Otherwise the line highlight doesn't work properly. - if (this._formatted && forceUnformatted) { - this.formatted = false; - setTimeout(this.revealPosition.bind(this), 0, position, textRangeToSelect); - return; - } - - if (!textRangeToSelect) - textRangeToSelect = new WebInspector.TextRange(position.lineNumber, position.columnNumber, position.lineNumber, position.columnNumber); - - function removeStyleClass() - { - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.HighlightedStyleClassName); - } - - function revealAndHighlightLine() - { - // If the line is not visible, reveal it as the center line in the editor. - var position = this._codeMirrorPositionFromTextRange(textRangeToSelect); - if (!this._isPositionVisible(position.start)) - this._scrollIntoViewCentered(position.start); - - this.selectedTextRange = textRangeToSelect; - - if (noHighlight) - return; - - this._codeMirror.addLineClass(lineHandle, "wrap", WebInspector.TextEditor.HighlightedStyleClassName); - - // Use a timeout instead of a webkitAnimationEnd event listener because the line element might - // be removed if the user scrolls during the animation. In that case webkitAnimationEnd isn't - // fired, and the line would highlight again the next time it scrolls into view. - setTimeout(removeStyleClass.bind(this), WebInspector.TextEditor.HighlightAnimationDuration); - } - - this._codeMirror.operation(revealAndHighlightLine.bind(this)); - }, - - updateLayout: function(force) - { - this._codeMirror.refresh(); - }, - - shown: function() - { - this._visible = true; - - // Refresh since our size might have changed. - this._codeMirror.refresh(); - - // Try revealing the pending line now that we are visible. - // This needs to be done as a separate operation from the refresh - // so that the scrollInfo coordinates are correct. - this._revealPendingPositionIfPossible(); - }, - - hidden: function() - { - this._visible = false; - }, - - setBreakpointInfoForLineAndColumn: function(lineNumber, columnNumber, breakpointInfo) - { - if (this._ignoreSetBreakpointInfoCalls) - return; - - if (breakpointInfo) - this._addBreakpointToLineAndColumnWithInfo(lineNumber, columnNumber, breakpointInfo); - else - this._removeBreakpointFromLineAndColumn(lineNumber, columnNumber); - }, - - updateBreakpointLineAndColumn: function(oldLineNumber, oldColumnNumber, newLineNumber, newColumnNumber) - { - console.assert(this._breakpoints[oldLineNumber]); - if (!this._breakpoints[oldLineNumber]) - return; - - console.assert(this._breakpoints[oldLineNumber][oldColumnNumber]); - if (!this._breakpoints[oldLineNumber][oldColumnNumber]) - return; - - var breakpointInfo = this._breakpoints[oldLineNumber][oldColumnNumber]; - this._removeBreakpointFromLineAndColumn(oldLineNumber, oldColumnNumber); - this._addBreakpointToLineAndColumnWithInfo(newLineNumber, newColumnNumber, breakpointInfo); - }, - - addStyleClassToLine: function(lineNumber, styleClassName) - { - var lineHandle = this._codeMirror.getLineHandle(lineNumber); - console.assert(lineHandle); - if (!lineHandle) - return; - - return this._codeMirror.addLineClass(lineHandle, "wrap", styleClassName); - }, - - removeStyleClassFromLine: function(lineNumber, styleClassName) - { - var lineHandle = this._codeMirror.getLineHandle(lineNumber); - console.assert(lineHandle); - if (!lineHandle) - return; - - return this._codeMirror.removeLineClass(lineHandle, "wrap", styleClassName); - }, - - toggleStyleClassForLine: function(lineNumber, styleClassName) - { - var lineHandle = this._codeMirror.getLineHandle(lineNumber); - console.assert(lineHandle); - if (!lineHandle) - return; - - return this._codeMirror.toggleLineClass(lineHandle, "wrap", styleClassName); - }, - - get lineCount() - { - return this._codeMirror.lineCount(); - }, - - focus: function() - { - this._codeMirror.focus(); - }, - - contentDidChange: function(replacedRanges, newRanges) - { - // Implemented by subclasses. - }, - - boundsForRange: function(range) - { - return this._codeMirror.boundsForRange(range); - }, - - get markers() - { - return this._codeMirror.getAllMarks().map(function(codeMirrorTextMarker) { - return WebInspector.TextMarker.textMarkerForCodeMirrorTextMarker(codeMirrorTextMarker); - }); - }, - - markersAtPosition: function(position) - { - return this._codeMirror.findMarksAt(position).map(function(codeMirrorTextMarker) { - return WebInspector.TextMarker.textMarkerForCodeMirrorTextMarker(codeMirrorTextMarker); - }); - }, - - createColorMarkers: function(lineNumber) - { - return this._codeMirror.createColorMarkers(lineNumber); - }, - - colorEditingControllerForMarker: function(colorMarker) - { - return new WebInspector.CodeMirrorColorEditingController(this._codeMirror, colorMarker); - }, - - // Private - - _contentChanged: function(codeMirror, change) - { - if (this._ignoreCodeMirrorContentDidChangeEvent > 0) - return; - - var replacedRanges = []; - var newRanges = []; - while (change) { - replacedRanges.push(new WebInspector.TextRange( - change.from.line, - change.from.ch, - change.to.line, - change.to.ch - )); - newRanges.push(new WebInspector.TextRange( - change.from.line, - change.from.ch, - change.from.line + change.text.length - 1, - change.text.length === 1 ? change.from.ch + change.text[0].length : change.text.lastValue.length - )); - change = change.next; - } - this.contentDidChange(replacedRanges, newRanges); - - if (this._formatted) { - this._formatterSourceMap = null; - this._formatted = false; - - if (this._delegate && typeof this._delegate.textEditorUpdatedFormatting === "function") - this._delegate.textEditorUpdatedFormatting(this); - - this.dispatchEventToListeners(WebInspector.TextEditor.Event.FormattingDidChange); - } - - this.dispatchEventToListeners(WebInspector.TextEditor.Event.ContentDidChange); - }, - - _textRangeFromCodeMirrorPosition: function(start, end) - { - console.assert(start); - console.assert(end); - - return new WebInspector.TextRange(start.line, start.ch, end.line, end.ch); - }, - - _codeMirrorPositionFromTextRange: function(textRange) - { - console.assert(textRange); - - var start = {line: textRange.startLine, ch: textRange.startColumn}; - var end = {line: textRange.endLine, ch: textRange.endColumn}; - return {start: start, end: end}; - }, - - _revealPendingPositionIfPossible: function() - { - // Nothing to do if we don't have a pending position. - if (!this._positionToReveal) - return; - - // Don't try to reveal unless we are visible. - if (!this._visible) - return; - - this.revealPosition(this._positionToReveal, this._textRangeToSelect, this._forceUnformatted); - }, - - _revealSearchResult: function(result, changeFocus, directionInCaseOfRevalidation) - { - var position = result.find(); - - // Check for a valid position, it might have been removed from editing by the user. - // If the position is invalide, revalidate all positions reveal as needed. - if (!position) { - this._revalidateSearchResults(directionInCaseOfRevalidation); - return; - } - - // If the line is not visible, reveal it as the center line in the editor. - if (!this._isPositionVisible(position.from)) - this._scrollIntoViewCentered(position.from); - - // Update the text selection to select the search result. - this.selectedTextRange = this._textRangeFromCodeMirrorPosition(position.from, position.to); - - // Remove the automatically reveal state now that we have revealed a search result. - this._automaticallyRevealFirstSearchResult = false; - - // Focus the editor if requested. - if (changeFocus) - this._codeMirror.focus(); - - // Remove the bouncy highlight if it is still around. The animation will not - // start unless we remove it and add it back to the document. - if (this._bouncyHighlightElement) - this._bouncyHighlightElement.remove(); - - // Create the bouncy highlight. - this._bouncyHighlightElement = document.createElement("div"); - this._bouncyHighlightElement.className = WebInspector.TextEditor.BouncyHighlightStyleClassName; - - // Collect info for the bouncy highlight. - var textContent = this._codeMirror.getSelection(); - var coordinates = this._codeMirror.cursorCoords(true, "page"); - - // Adjust the coordinates to be based in the text editor's space. - var textEditorRect = this._element.getBoundingClientRect(); - coordinates.top -= textEditorRect.top; - coordinates.left -= textEditorRect.left; - - // Position and show the bouncy highlight. - this._bouncyHighlightElement.textContent = textContent; - this._bouncyHighlightElement.style.top = coordinates.top + "px"; - this._bouncyHighlightElement.style.left = coordinates.left + "px"; - this._element.appendChild(this._bouncyHighlightElement); - - function animationEnded() - { - if (!this._bouncyHighlightElement) - return; - - this._bouncyHighlightElement.remove(); - delete this._bouncyHighlightElement; - } - - // Listen for the end of the animation so we can remove the element. - this._bouncyHighlightElement.addEventListener("webkitAnimationEnd", animationEnded.bind(this)); - }, - - _binarySearchInsertionIndexInSearchResults: function(object, comparator) - { - // It is possible that markers in the search results array may have been deleted. - // In those cases the comparator will return "null" and we immediately stop - // the binary search and return null. The search results list needs to be updated. - var array = this._searchResults; - - var first = 0; - var last = array.length - 1; - - while (first <= last) { - var mid = (first + last) >> 1; - var c = comparator(object, array[mid]); - if (c === null) - return null; - if (c > 0) - first = mid + 1; - else if (c < 0) - last = mid - 1; - else - return mid; - } - - return first - 1; - }, - - _revealFirstSearchResultBeforeCursor: function(changeFocus) - { - console.assert(this._searchResults.length); - - var currentCursorPosition = this._codeMirror.getCursor("start"); - if (currentCursorPosition.line === 0 && currentCursorPosition.ch === 0) { - this._currentSearchResultIndex = this._searchResults.length - 1; - this._revealSearchResult(this._searchResults[this._currentSearchResultIndex], changeFocus, -1); - return; - } - - var index = this._binarySearchInsertionIndexInSearchResults(currentCursorPosition, function(current, searchResult) { - var searchResultMarker = searchResult.find(); - if (!searchResultMarker) - return null; - return WebInspector.compareCodeMirrorPositions(current, searchResultMarker.from); - }); - - if (index === null) { - this._revalidateSearchResults(-1); - return; - } - - this._currentSearchResultIndex = index; - this._revealSearchResult(this._searchResults[this._currentSearchResultIndex], changeFocus); - }, - - _revealFirstSearchResultAfterCursor: function(changeFocus) - { - console.assert(this._searchResults.length); - - var currentCursorPosition = this._codeMirror.getCursor("start"); - if (currentCursorPosition.line === 0 && currentCursorPosition.ch === 0) { - this._currentSearchResultIndex = 0; - this._revealSearchResult(this._searchResults[this._currentSearchResultIndex], changeFocus, 1); - return; - } - - var index = this._binarySearchInsertionIndexInSearchResults(currentCursorPosition, function(current, searchResult) { - var searchResultMarker = searchResult.find(); - if (!searchResultMarker) - return null; - return WebInspector.compareCodeMirrorPositions(current, searchResultMarker.from); - }); - - if (index === null) { - this._revalidateSearchResults(1); - return; - } - - if (index + 1 < this._searchResults.length) - ++index; - else - index = 0; - - this._currentSearchResultIndex = index; - this._revealSearchResult(this._searchResults[this._currentSearchResultIndex], changeFocus); - }, - - _cursorDoesNotMatchLastRevealedSearchResult: function() - { - console.assert(this._currentSearchResultIndex !== -1); - console.assert(this._searchResults.length); - - var lastRevealedSearchResultMarker = this._searchResults[this._currentSearchResultIndex].find(); - if (!lastRevealedSearchResultMarker) - return true; - - var currentCursorPosition = this._codeMirror.getCursor("start"); - var lastRevealedSearchResultPosition = lastRevealedSearchResultMarker.from; - - return WebInspector.compareCodeMirrorPositions(currentCursorPosition, lastRevealedSearchResultPosition) !== 0; - }, - - _revalidateSearchResults: function(direction) - { - console.assert(direction !== undefined); - - this._currentSearchResultIndex = -1; - - var updatedSearchResults = []; - for (var i = 0; i < this._searchResults.length; ++i) { - if (this._searchResults[i].find()) - updatedSearchResults.push(this._searchResults[i]); - } - - console.assert(updatedSearchResults.length !== this._searchResults.length); - - this._searchResults = updatedSearchResults; - this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange); - - if (this._searchResults.length) { - if (direction > 0) - this._revealFirstSearchResultAfterCursor(); - else - this._revealFirstSearchResultBeforeCursor(); - } - }, - - _updateExecutionLine: function() - { - function update() - { - if (this._executionLineHandle) - this._codeMirror.removeLineClass(this._executionLineHandle, "wrap", WebInspector.TextEditor.ExecutionLineStyleClassName); - - this._executionLineHandle = !isNaN(this._executionLineNumber) ? this._codeMirror.getLineHandle(this._executionLineNumber) : null; - - if (this._executionLineHandle) - this._codeMirror.addLineClass(this._executionLineHandle, "wrap", WebInspector.TextEditor.ExecutionLineStyleClassName); - } - - this._codeMirror.operation(update.bind(this)); - }, - - _setBreakpointStylesOnLine: function(lineNumber) - { - var columnBreakpoints = this._breakpoints[lineNumber]; - console.assert(columnBreakpoints); - if (!columnBreakpoints) - return; - - var allDisabled = true; - var allResolved = true; - var allAutoContinue = true; - var multiple = Object.keys(columnBreakpoints).length > 1; - for (var columnNumber in columnBreakpoints) { - var breakpointInfo = columnBreakpoints[columnNumber]; - if (!breakpointInfo.disabled) - allDisabled = false; - if (!breakpointInfo.resolved) - allResolved = false; - if (!breakpointInfo.autoContinue) - allAutoContinue = false; - } - - function updateStyles() - { - // We might not have a line if the content isn't fully populated yet. - // This will be called again when the content is available. - var lineHandle = this._codeMirror.getLineHandle(lineNumber); - if (!lineHandle) - return; - - this._codeMirror.addLineClass(lineHandle, "wrap", WebInspector.TextEditor.HasBreakpointStyleClassName); - - if (allResolved) - this._codeMirror.addLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointResolvedStyleClassName); - else - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointResolvedStyleClassName); - - if (allDisabled) - this._codeMirror.addLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointDisabledStyleClassName); - else - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointDisabledStyleClassName); - - if (allAutoContinue) - this._codeMirror.addLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointAutoContinueStyleClassName); - else - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointAutoContinueStyleClassName); - - if (multiple) - this._codeMirror.addLineClass(lineHandle, "wrap", WebInspector.TextEditor.MultipleBreakpointsStyleClassName); - else - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.MultipleBreakpointsStyleClassName); - } - - this._codeMirror.operation(updateStyles.bind(this)); - }, - - _addBreakpointToLineAndColumnWithInfo: function(lineNumber, columnNumber, breakpointInfo) - { - if (!this._breakpoints[lineNumber]) - this._breakpoints[lineNumber] = {}; - this._breakpoints[lineNumber][columnNumber] = breakpointInfo; - - this._setBreakpointStylesOnLine(lineNumber); - }, - - _removeBreakpointFromLineAndColumn: function(lineNumber, columnNumber) - { - console.assert(columnNumber in this._breakpoints[lineNumber]); - delete this._breakpoints[lineNumber][columnNumber]; - - // There are still breakpoints on the line. Update the breakpoint style. - if (!isEmptyObject(this._breakpoints[lineNumber])) { - this._setBreakpointStylesOnLine(lineNumber); - return; - } - - delete this._breakpoints[lineNumber]; - - function updateStyles() - { - var lineHandle = this._codeMirror.getLineHandle(lineNumber); - if (!lineHandle) - return; - - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.HasBreakpointStyleClassName); - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointResolvedStyleClassName); - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointDisabledStyleClassName); - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.BreakpointAutoContinueStyleClassName); - this._codeMirror.removeLineClass(lineHandle, "wrap", WebInspector.TextEditor.MultipleBreakpointsStyleClassName); - } - - this._codeMirror.operation(updateStyles.bind(this)); - }, - - _allColumnBreakpointInfoForLine: function(lineNumber) - { - return this._breakpoints[lineNumber]; - }, - - _setColumnBreakpointInfoForLine: function(lineNumber, columnBreakpointInfo) - { - console.assert(columnBreakpointInfo); - this._breakpoints[lineNumber] = columnBreakpointInfo; - this._setBreakpointStylesOnLine(lineNumber); - }, - - _gutterMouseDown: function(codeMirror, lineNumber, gutterElement, event) - { - if (event.button !== 0 || event.ctrlKey) - return; - - if (!this._codeMirror.hasLineClass(lineNumber, "wrap", WebInspector.TextEditor.HasBreakpointStyleClassName)) { - console.assert(!(lineNumber in this._breakpoints)); - - // No breakpoint, add a new one. - if (this._delegate && typeof this._delegate.textEditorBreakpointAdded === "function") { - var data = this._delegate.textEditorBreakpointAdded(this, lineNumber, 0); - if (data) { - var breakpointInfo = data.breakpointInfo; - if (breakpointInfo) - this._addBreakpointToLineAndColumnWithInfo(data.lineNumber, data.columnNumber, breakpointInfo); - } - } - - return; - } - - console.assert(lineNumber in this._breakpoints); - - if (this._codeMirror.hasLineClass(lineNumber, "wrap", WebInspector.TextEditor.MultipleBreakpointsStyleClassName)) { - console.assert(!isEmptyObject(this._breakpoints[lineNumber])); - return; - } - - // Single existing breakpoint, start tracking it for dragging. - console.assert(Object.keys(this._breakpoints[lineNumber]).length === 1); - var columnNumber = Object.keys(this._breakpoints[lineNumber])[0]; - this._draggingBreakpointInfo = this._breakpoints[lineNumber][columnNumber]; - this._lineNumberWithMousedDownBreakpoint = lineNumber; - this._lineNumberWithDraggedBreakpoint = lineNumber; - this._columnNumberWithMousedDownBreakpoint = columnNumber; - this._columnNumberWithDraggedBreakpoint = columnNumber; - - this._documentMouseMovedEventListener = this._documentMouseMoved.bind(this); - this._documentMouseUpEventListener = this._documentMouseUp.bind(this); - - // Register these listeners on the document so we can track the mouse if it leaves the gutter. - document.addEventListener("mousemove", this._documentMouseMovedEventListener, true); - document.addEventListener("mouseup", this._documentMouseUpEventListener, true); - }, - - _gutterContextMenu: function(codeMirror, lineNumber, gutterElement, event) - { - if (this._delegate && typeof this._delegate.textEditorGutterContextMenu === "function") { - var breakpoints = []; - for (var columnNumber in this._breakpoints[lineNumber]) - breakpoints.push({lineNumber:lineNumber, columnNumber:columnNumber}); - - this._delegate.textEditorGutterContextMenu(this, lineNumber, 0, breakpoints, event); - } - }, - - _documentMouseMoved: function(event) - { - console.assert("_lineNumberWithMousedDownBreakpoint" in this); - if (!("_lineNumberWithMousedDownBreakpoint" in this)) - return; - - event.preventDefault(); - - var lineNumber; - var position = this._codeMirror.coordsChar({left: event.pageX, top: event.pageY}); - - // CodeMirror's coordsChar returns a position even if it is outside the bounds. Nullify the position - // if the event is outside the bounds of the gutter so we will remove the breakpoint. - var gutterBounds = this._codeMirror.getGutterElement().getBoundingClientRect(); - if (event.pageX < gutterBounds.left || event.pageX > gutterBounds.right || event.pageY < gutterBounds.top || event.pageY > gutterBounds.bottom) - position = null; - - // If we have a position and it has a line then use it. - if (position && "line" in position) - lineNumber = position.line; - - // The _lineNumberWithDraggedBreakpoint property can be undefined if the user drags - // outside of the gutter. The lineNumber variable can be undefined for the same reason. - - if (lineNumber === this._lineNumberWithDraggedBreakpoint) - return; - - // Record that the mouse dragged some so when mouse up fires we know to do the - // work of removing and moving the breakpoint. - this._mouseDragged = true; - - if ("_lineNumberWithDraggedBreakpoint" in this) { - // We have a line that is currently showing the dragged breakpoint. Remove that breakpoint - // and restore the previous one (if any.) - if (this._previousColumnBreakpointInfo) - this._setColumnBreakpointInfoForLine(this._lineNumberWithDraggedBreakpoint, this._previousColumnBreakpointInfo); - else - this._removeBreakpointFromLineAndColumn(this._lineNumberWithDraggedBreakpoint, this._columnNumberWithDraggedBreakpoint); - - delete this._previousColumnBreakpointInfo; - delete this._lineNumberWithDraggedBreakpoint; - delete this._columnNumberWithDraggedBreakpoint; - } - - if (lineNumber !== undefined) { - // We have a new line that will now show the dragged breakpoint. - var newColumnBreakpoints = {}; - var columnNumber = (lineNumber === this._lineNumberWithMousedDownBreakpoint ? this._columnNumberWithDraggedBreakpoint : 0); - newColumnBreakpoints[columnNumber] = this._draggingBreakpointInfo; - this._previousColumnBreakpointInfo = this._allColumnBreakpointInfoForLine(lineNumber); - this._setColumnBreakpointInfoForLine(lineNumber, newColumnBreakpoints); - this._lineNumberWithDraggedBreakpoint = lineNumber; - this._columnNumberWithDraggedBreakpoint = columnNumber; - } - }, - - _documentMouseUp: function(event) - { - console.assert("_lineNumberWithMousedDownBreakpoint" in this); - if (!("_lineNumberWithMousedDownBreakpoint" in this)) - return; - - event.preventDefault(); - - document.removeEventListener("mousemove", this._documentMouseMovedEventListener, true); - document.removeEventListener("mouseup", this._documentMouseUpEventListener, true); - - const delegateImplementsBreakpointClicked = this._delegate && typeof this._delegate.textEditorBreakpointClicked === "function"; - const delegateImplementsBreakpointRemoved = this._delegate && typeof this._delegate.textEditorBreakpointRemoved === "function"; - const delegateImplementsBreakpointMoved = this._delegate && typeof this._delegate.textEditorBreakpointMoved === "function"; - - if (this._mouseDragged) { - if (!("_lineNumberWithDraggedBreakpoint" in this)) { - // The breakpoint was dragged off the gutter, remove it. - if (delegateImplementsBreakpointRemoved) { - this._ignoreSetBreakpointInfoCalls = true; - this._delegate.textEditorBreakpointRemoved(this, this._lineNumberWithMousedDownBreakpoint, this._columnNumberWithMousedDownBreakpoint); - delete this._ignoreSetBreakpointInfoCalls; - } - } else if (this._lineNumberWithMousedDownBreakpoint !== this._lineNumberWithDraggedBreakpoint) { - // The dragged breakpoint was moved to a new line. - - // If there is are breakpoints already at the drop line, tell the delegate to remove them. - // We have already updated the breakpoint info internally, so when the delegate removes the breakpoints - // and tells us to clear the breakpoint info, we can ignore those calls. - if (this._previousColumnBreakpointInfo && delegateImplementsBreakpointRemoved) { - this._ignoreSetBreakpointInfoCalls = true; - for (var columnNumber in this._previousColumnBreakpointInfo) - this._delegate.textEditorBreakpointRemoved(this, this._lineNumberWithDraggedBreakpoint, columnNumber); - delete this._ignoreSetBreakpointInfoCalls; - } - - // Tell the delegate to move the breakpoint from one line to another. - if (delegateImplementsBreakpointMoved) { - this._ignoreSetBreakpointInfoCalls = true; - this._delegate.textEditorBreakpointMoved(this, this._lineNumberWithMousedDownBreakpoint, this._columnNumberWithMousedDownBreakpoint, this._lineNumberWithDraggedBreakpoint, this._columnNumberWithDraggedBreakpoint); - delete this._ignoreSetBreakpointInfoCalls; - } - } - } else { - // Toggle the disabled state of the breakpoint. - console.assert(this._lineNumberWithMousedDownBreakpoint in this._breakpoints); - console.assert(this._columnNumberWithMousedDownBreakpoint in this._breakpoints[this._lineNumberWithMousedDownBreakpoint]); - if (this._lineNumberWithMousedDownBreakpoint in this._breakpoints && this._columnNumberWithMousedDownBreakpoint in this._breakpoints[this._lineNumberWithMousedDownBreakpoint] && delegateImplementsBreakpointClicked) - this._delegate.textEditorBreakpointClicked(this, this._lineNumberWithMousedDownBreakpoint, this._columnNumberWithMousedDownBreakpoint); - } - - delete this._documentMouseMovedEventListener; - delete this._documentMouseUpEventListener; - delete this._lineNumberWithMousedDownBreakpoint; - delete this._lineNumberWithDraggedBreakpoint; - delete this._columnNumberWithMousedDownBreakpoint; - delete this._columnNumberWithDraggedBreakpoint; - delete this._previousColumnBreakpointInfo; - delete this._mouseDragged; - }, - - _openClickedLinks: function(event) - { - // Get the position in the text and the token at that position. - var position = this._codeMirror.coordsChar({left: event.pageX, top: event.pageY}); - var tokenInfo = this._codeMirror.getTokenAt(position); - if (!tokenInfo || !tokenInfo.type || !tokenInfo.string) - return; - - // If the token is not a link, then ignore it. - if (!/\blink\b/.test(tokenInfo.type)) - return; - - // The token string is the URL we should open. It might be a relative URL. - var url = tokenInfo.string; - - // Get the base URL. - var baseURL = ""; - if (this._delegate && typeof this._delegate.textEditorBaseURL === "function") - baseURL = this._delegate.textEditorBaseURL(this); - - // Open the link after resolving the absolute URL from the base URL. - WebInspector.openURL(absoluteURL(url, baseURL)); - - // Stop processing the event. - event.preventDefault(); - event.stopPropagation(); - }, - - _isPositionVisible: function(position) - { - var scrollInfo = this._codeMirror.getScrollInfo(); - var visibleRangeStart = scrollInfo.top; - var visibleRangeEnd = visibleRangeStart + scrollInfo.clientHeight; - var coords = this._codeMirror.charCoords(position, "local"); - - return coords.top >= visibleRangeStart && coords.bottom <= visibleRangeEnd; - }, - - _scrollIntoViewCentered: function(position) - { - var scrollInfo = this._codeMirror.getScrollInfo(); - var lineHeight = Math.ceil(this._codeMirror.defaultTextHeight()); - var margin = Math.floor((scrollInfo.clientHeight - lineHeight) / 2); - this._codeMirror.scrollIntoView(position, margin); - }, - - _prettyPrint: function(pretty) - { - function prettyPrintAndUpdateEditor() - { - const start = {line: 0, ch: 0}; - const end = {line: this._codeMirror.lineCount() - 1}; - - var oldSelectionAnchor = this._codeMirror.getCursor("anchor"); - var oldSelectionHead = this._codeMirror.getCursor("head"); - var newSelectionAnchor, newSelectionHead; - var newExecutionLocation = null; - - if (pretty) { - // <rdar://problem/10593948> Provide a way to change the tab width in the Web Inspector - const indentString = " "; - var originalLineEndings = []; - var formattedLineEndings = []; - var mapping = {original: [0], formatted: [0]}; - var builder = new FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, 0, 0, indentString); - var formatter = new Formatter(this._codeMirror, builder); - formatter.format(start, end); - - this._formatterSourceMap = WebInspector.FormatterSourceMap.fromBuilder(builder); - - this._codeMirror.setValue(builder.formattedContent); - - if (this._positionToReveal) { - var newRevealPosition = this._formatterSourceMap.originalToFormatted(this._positionToReveal.lineNumber, this._positionToReveal.columnNumber); - this._positionToReveal = new WebInspector.SourceCodePosition(newRevealPosition.lineNumber, newRevealPosition.columnNumber); - } - - if (this._textRangeToSelect) { - var mappedRevealSelectionStart = this._formatterSourceMap.originalToFormatted(this._textRangeToSelect.startLine, this._textRangeToSelect.startColumn); - var mappedRevealSelectionEnd = this._formatterSourceMap.originalToFormatted(this._textRangeToSelect.endLine, this._textRangeToSelect.endColumn); - this._textRangeToSelect = new WebInspector.TextRange(mappedRevealSelectionStart.lineNumber, mappedRevealSelectionStart.columnNumber, mappedRevealSelectionEnd.lineNumber, mappedRevealSelectionEnd.columnNumber); - } - - if (!isNaN(this._executionLineNumber)) { - console.assert(!isNaN(this._executionColumnNumber)); - newExecutionLocation = this._formatterSourceMap.originalToFormatted(this._executionLineNumber, this._executionColumnNumber); - } - - var mappedAnchorLocation = this._formatterSourceMap.originalToFormatted(oldSelectionAnchor.line, oldSelectionAnchor.ch); - var mappedHeadLocation = this._formatterSourceMap.originalToFormatted(oldSelectionHead.line, oldSelectionHead.ch); - newSelectionAnchor = {line:mappedAnchorLocation.lineNumber, ch:mappedAnchorLocation.columnNumber}; - newSelectionHead = {line:mappedHeadLocation.lineNumber, ch:mappedHeadLocation.columnNumber}; - } else { - this._codeMirror.undo(); - - if (this._positionToReveal) { - var newRevealPosition = this._formatterSourceMap.formattedToOriginal(this._positionToReveal.lineNumber, this._positionToReveal.columnNumber); - this._positionToReveal = new WebInspector.SourceCodePosition(newRevealPosition.lineNumber, newRevealPosition.columnNumber); - } - - if (this._textRangeToSelect) { - var mappedRevealSelectionStart = this._formatterSourceMap.formattedToOriginal(this._textRangeToSelect.startLine, this._textRangeToSelect.startColumn); - var mappedRevealSelectionEnd = this._formatterSourceMap.formattedToOriginal(this._textRangeToSelect.endLine, this._textRangeToSelect.endColumn); - this._textRangeToSelect = new WebInspector.TextRange(mappedRevealSelectionStart.lineNumber, mappedRevealSelectionStart.columnNumber, mappedRevealSelectionEnd.lineNumber, mappedRevealSelectionEnd.columnNumber); - } - - if (!isNaN(this._executionLineNumber)) { - console.assert(!isNaN(this._executionColumnNumber)); - newExecutionLocation = this._formatterSourceMap.formattedToOriginal(this._executionLineNumber, this._executionColumnNumber); - } - - var mappedAnchorLocation = this._formatterSourceMap.formattedToOriginal(oldSelectionAnchor.line, oldSelectionAnchor.ch); - var mappedHeadLocation = this._formatterSourceMap.formattedToOriginal(oldSelectionHead.line, oldSelectionHead.ch); - newSelectionAnchor = {line:mappedAnchorLocation.lineNumber, ch:mappedAnchorLocation.columnNumber}; - newSelectionHead = {line:mappedHeadLocation.lineNumber, ch:mappedHeadLocation.columnNumber}; - - this._formatterSourceMap = null; - } - - this._scrollIntoViewCentered(newSelectionAnchor); - this._codeMirror.setSelection(newSelectionAnchor, newSelectionHead); - - if (newExecutionLocation) { - delete this._executionLineHandle; - this.executionColumnNumber = newExecutionLocation.columnNumber; - this.executionLineNumber = newExecutionLocation.lineNumber; - } - - // FIXME: <rdar://problem/13129955> FindBanner: New searches should not lose search position (start from current selection/caret) - if (this.currentSearchQuery) { - var searchQuery = this.currentSearchQuery; - this.searchCleared(); - // Set timeout so that this happens after the current CodeMirror operation. - // The editor has to update for the value and selection changes. - setTimeout(function(query) { - this.performSearch(searchQuery); - }.bind(this), 0); - } - - if (this._delegate && typeof this._delegate.textEditorUpdatedFormatting === "function") - this._delegate.textEditorUpdatedFormatting(this); - } - - this._codeMirror.operation(prettyPrintAndUpdateEditor.bind(this)); - } -}; - -WebInspector.TextEditor.prototype.__proto__ = WebInspector.Object.prototype; |