Web Inspector: Save scroll selection and cursor position of SourceFrames in sources...
authorvsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jul 2012 09:28:41 +0000 (09:28 +0000)
committervsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jul 2012 09:28:41 +0000 (09:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=90294

Reviewed by Yury Semikhatsky.

Source/WebCore:

Scroll and selection change handling is now delegated from TextViewer to SourceFrame.
SourceFrame now dispatches ScrollChanged and SelectionChanged events.
TabbedEditorContainer now saves scroll and selection information together
with the url history and restores scroll and selection on resource opening.

* inspector/front-end/JavaScriptSourceFrame.js:
(WebInspector.JavaScriptSourceFrame.prototype.setExecutionLine):
* inspector/front-end/SourceFrame.js:
(WebInspector.SourceFrame.prototype.wasShown):
(WebInspector.SourceFrame.prototype.highlightLine):
(WebInspector.SourceFrame.prototype._innerHighlightLineIfNeeded):
(WebInspector.SourceFrame.prototype._clearLineHighlight):
(WebInspector.SourceFrame.prototype.revealLine):
(WebInspector.SourceFrame.prototype._innerRevealLineIfNeeded):
(WebInspector.SourceFrame.prototype._clearLineToReveal):
(WebInspector.SourceFrame.prototype.scrollToLine):
(WebInspector.SourceFrame.prototype._innerScrollToLineIfNeeded):
(WebInspector.SourceFrame.prototype._clearLineToScrollTo):
(WebInspector.SourceFrame.prototype.setSelection):
(WebInspector.SourceFrame.prototype._innerSetSelectionIfNeeded):
(WebInspector.SourceFrame.prototype._wasShownOrLoaded):
(WebInspector.SourceFrame.prototype.setContent):
(WebInspector.SourceFrame.prototype.commitEditing):
(WebInspector.SourceFrame.prototype.selectionChanged):
(WebInspector.SourceFrame.prototype.scrollChanged):
(WebInspector.TextViewerDelegateForSourceFrame.prototype.selectionChanged):
(WebInspector.TextViewerDelegateForSourceFrame.prototype.scrollChanged):
* inspector/front-end/TabbedEditorContainer.js:
(WebInspector.TabbedEditorContainer):
(WebInspector.TabbedEditorContainer.prototype._addScrollAndSelectionListeners):
(WebInspector.TabbedEditorContainer.prototype._removeScrollAndSelectionListeners):
(WebInspector.TabbedEditorContainer.prototype._scrollChanged):
(WebInspector.TabbedEditorContainer.prototype._selectionChanged):
(WebInspector.TabbedEditorContainer.prototype._appendFileTab):
(WebInspector.TabbedEditorContainer.prototype._tabClosed):
(WebInspector.TabbedEditorContainer.HistoryItem):
(WebInspector.TabbedEditorContainer.HistoryItem.fromObject):
(WebInspector.TabbedEditorContainer.HistoryItem.prototype.serializeToObject):
(WebInspector.TabbedEditorContainer.History):
(WebInspector.TabbedEditorContainer.History.fromObject):
(WebInspector.TabbedEditorContainer.History.prototype.index):
(WebInspector.TabbedEditorContainer.History.prototype.selectionRange):
(WebInspector.TabbedEditorContainer.History.prototype.updateSelectionRange):
(WebInspector.TabbedEditorContainer.History.prototype.scrollLineNumber):
(WebInspector.TabbedEditorContainer.History.prototype.updateScrollLineNumber):
(WebInspector.TabbedEditorContainer.History.prototype.update):
(WebInspector.TabbedEditorContainer.History.prototype.remove):
(WebInspector.TabbedEditorContainer.History.prototype.save):
(WebInspector.TabbedEditorContainer.History.prototype.set _serializeToObject):
* inspector/front-end/TextEditorModel.js:
(WebInspector.TextRange.fromObject):
(WebInspector.TextRange.prototype.clone):
(WebInspector.TextRange.prototype.serializeToObject):
* inspector/front-end/TextViewer.js:
(WebInspector.TextViewer.prototype._handleScrollChanged):
(WebInspector.TextViewer.prototype.scrollToLine):
(WebInspector.TextViewer.prototype._handleSelectionChange):
(WebInspector.TextViewer.prototype.setSelection):
(WebInspector.TextViewer.prototype.wasShown):
(WebInspector.TextViewer.prototype._handleFocused):
(WebInspector.TextViewer.prototype.willHide):
(WebInspector.TextViewerDelegate.prototype.selectionChanged):
(WebInspector.TextViewerDelegate.prototype.scrollChanged):
(WebInspector.TextEditorChunkedPanel.prototype.scrollToLine):

LayoutTests:

* inspector/tabbed-editors-history-expected.txt:
* inspector/tabbed-editors-history.html:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@121750 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/inspector/tabbed-editors-history-expected.txt
LayoutTests/inspector/tabbed-editors-history.html
Source/WebCore/ChangeLog
Source/WebCore/inspector/front-end/JavaScriptSourceFrame.js
Source/WebCore/inspector/front-end/SourceFrame.js
Source/WebCore/inspector/front-end/TabbedEditorContainer.js
Source/WebCore/inspector/front-end/TextEditor.js
Source/WebCore/inspector/front-end/TextEditorModel.js

index 0ac4257..b3eee91 100644 (file)
@@ -1,3 +1,13 @@
+2012-07-03  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: Save scroll selection and cursor position of SourceFrames in sources panel.
+        https://bugs.webkit.org/show_bug.cgi?id=90294
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/tabbed-editors-history-expected.txt:
+        * inspector/tabbed-editors-history.html:
+
 2012-07-03  Taiju Tsuiki  <tzik@chromium.org>
 
         Web Inspector: Make DirectoryContentView sortable
index 7f8beee..36291ce 100644 (file)
@@ -2,26 +2,27 @@ Tests history saving logic in TabbedEditorContainer.
 
 Bug 76912
   history = []
-  history = [url_1]
-  history = [url_2,url_1]
-  history = [url_3,url_2,url_1]
-  history = [url_2,url_3,url_1]
-  history = [url_1,url_2,url_3]
-  history = [url_11,url_1,url_2,url_3]
-  history = [url_12,url_11,url_1,url_2,url_3]
-  history = [url_13,url_12,url_11,url_1,url_2,url_3]
-  history = [url_12,url_13,url_11,url_1,url_2,url_3]
-  history = [url_11,url_12,url_13,url_1,url_2,url_3]
-  history = [url_12,url_13,url_1,url_2,url_3]
-  history = [url_12,url_1,url_2,url_3]
-  history = [url_14,url_12,url_1,url_2,url_3]
-  history = [url_15,url_14,url_12,url_1,url_2,url_3]
-  history = [url_16,url_15,url_14,url_12,url_1,url_2,url_3]
-  history = [url_15,url_14,url_12,url_1,url_2,url_3]
-  history = [url_14,url_12,url_1,url_2,url_3]
-  history = [url_12,url_1,url_2,url_3]
-  history = [url_1,url_2,url_3]
-  history = [url_2,url_3]
-  history = [url_3]
+  history = [{"url":"url_1"}]
+  history = [{"url":"url_2"},{"url":"url_1"}]
+  history = [{"url":"url_3"},{"url":"url_2"},{"url":"url_1"}]
+  history = [{"url":"url_2"},{"url":"url_3"},{"url":"url_1"}]
+  history = [{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_11"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_12"},{"url":"url_11"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_13"},{"url":"url_12"},{"url":"url_11"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_12"},{"url":"url_13"},{"url":"url_11"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_11"},{"url":"url_12"},{"url":"url_13"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_11","selectionRange":{"startLine":15,"startColumn":5,"endLine":15,"endColumn":10},"scrollLineNumber":10},{"url":"url_12"},{"url":"url_13"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_12"},{"url":"url_13"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_12"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_14"},{"url":"url_12"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_15"},{"url":"url_14"},{"url":"url_12"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_16"},{"url":"url_15"},{"url":"url_14"},{"url":"url_12"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_15"},{"url":"url_14"},{"url":"url_12"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_14"},{"url":"url_12"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_12"},{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_1"},{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_2"},{"url":"url_3"}]
+  history = [{"url":"url_3"}]
   history = []
 
index 12c421e..3c4ee17 100644 (file)
@@ -6,7 +6,14 @@ var test = function()
 {
     function dumpHistory(history)
     {
-        InspectorTest.addResult("  history = [" + String(history._urls) + "]");
+        InspectorTest.addResult("  history = " + JSON.stringify(history._serializeToObject()) + "");
+    }
+
+    function updateScrollAndSelectionAndDump(history, url, scrollLineNumber, selection)
+    {
+        history.updateScrollLineNumber(url, scrollLineNumber);
+        history.updateSelectionRange(url, selection);
+        dumpHistory(history);
     }
 
     function updateAndDump(history, urls)
@@ -43,6 +50,7 @@ var test = function()
     // ... and switching between them.
     updateAndDump(history, [url(12), url(13), url(11)]);
     updateAndDump(history, [url(11), url(12), url(13)]);
+    updateScrollAndSelectionAndDump(history, url(11), 10, new WebInspector.TextRange(15, 5, 15, 10));
     // Now close some tabs.
     removeAndDump(history, url(11));
     removeAndDump(history, url(13));
index 73e6222..7fbe713 100644 (file)
@@ -1,3 +1,75 @@
+2012-07-03  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: Save scroll selection and cursor position of SourceFrames in sources panel.
+        https://bugs.webkit.org/show_bug.cgi?id=90294
+
+        Reviewed by Yury Semikhatsky.
+
+        Scroll and selection change handling is now delegated from TextViewer to SourceFrame.
+        SourceFrame now dispatches ScrollChanged and SelectionChanged events.
+        TabbedEditorContainer now saves scroll and selection information together
+        with the url history and restores scroll and selection on resource opening.
+
+        * inspector/front-end/JavaScriptSourceFrame.js:
+        (WebInspector.JavaScriptSourceFrame.prototype.setExecutionLine):
+        * inspector/front-end/SourceFrame.js:
+        (WebInspector.SourceFrame.prototype.wasShown):
+        (WebInspector.SourceFrame.prototype.highlightLine):
+        (WebInspector.SourceFrame.prototype._innerHighlightLineIfNeeded):
+        (WebInspector.SourceFrame.prototype._clearLineHighlight):
+        (WebInspector.SourceFrame.prototype.revealLine):
+        (WebInspector.SourceFrame.prototype._innerRevealLineIfNeeded):
+        (WebInspector.SourceFrame.prototype._clearLineToReveal):
+        (WebInspector.SourceFrame.prototype.scrollToLine):
+        (WebInspector.SourceFrame.prototype._innerScrollToLineIfNeeded):
+        (WebInspector.SourceFrame.prototype._clearLineToScrollTo):
+        (WebInspector.SourceFrame.prototype.setSelection):
+        (WebInspector.SourceFrame.prototype._innerSetSelectionIfNeeded):
+        (WebInspector.SourceFrame.prototype._wasShownOrLoaded):
+        (WebInspector.SourceFrame.prototype.setContent):
+        (WebInspector.SourceFrame.prototype.commitEditing):
+        (WebInspector.SourceFrame.prototype.selectionChanged):
+        (WebInspector.SourceFrame.prototype.scrollChanged):
+        (WebInspector.TextViewerDelegateForSourceFrame.prototype.selectionChanged):
+        (WebInspector.TextViewerDelegateForSourceFrame.prototype.scrollChanged):
+        * inspector/front-end/TabbedEditorContainer.js:
+        (WebInspector.TabbedEditorContainer):
+        (WebInspector.TabbedEditorContainer.prototype._addScrollAndSelectionListeners):
+        (WebInspector.TabbedEditorContainer.prototype._removeScrollAndSelectionListeners):
+        (WebInspector.TabbedEditorContainer.prototype._scrollChanged):
+        (WebInspector.TabbedEditorContainer.prototype._selectionChanged):
+        (WebInspector.TabbedEditorContainer.prototype._appendFileTab):
+        (WebInspector.TabbedEditorContainer.prototype._tabClosed):
+        (WebInspector.TabbedEditorContainer.HistoryItem):
+        (WebInspector.TabbedEditorContainer.HistoryItem.fromObject):
+        (WebInspector.TabbedEditorContainer.HistoryItem.prototype.serializeToObject):
+        (WebInspector.TabbedEditorContainer.History):
+        (WebInspector.TabbedEditorContainer.History.fromObject):
+        (WebInspector.TabbedEditorContainer.History.prototype.index):
+        (WebInspector.TabbedEditorContainer.History.prototype.selectionRange):
+        (WebInspector.TabbedEditorContainer.History.prototype.updateSelectionRange):
+        (WebInspector.TabbedEditorContainer.History.prototype.scrollLineNumber):
+        (WebInspector.TabbedEditorContainer.History.prototype.updateScrollLineNumber):
+        (WebInspector.TabbedEditorContainer.History.prototype.update):
+        (WebInspector.TabbedEditorContainer.History.prototype.remove):
+        (WebInspector.TabbedEditorContainer.History.prototype.save):
+        (WebInspector.TabbedEditorContainer.History.prototype.set _serializeToObject):
+        * inspector/front-end/TextEditorModel.js:
+        (WebInspector.TextRange.fromObject):
+        (WebInspector.TextRange.prototype.clone):
+        (WebInspector.TextRange.prototype.serializeToObject):
+        * inspector/front-end/TextViewer.js:
+        (WebInspector.TextViewer.prototype._handleScrollChanged):
+        (WebInspector.TextViewer.prototype.scrollToLine):
+        (WebInspector.TextViewer.prototype._handleSelectionChange):
+        (WebInspector.TextViewer.prototype.setSelection):
+        (WebInspector.TextViewer.prototype.wasShown):
+        (WebInspector.TextViewer.prototype._handleFocused):
+        (WebInspector.TextViewer.prototype.willHide):
+        (WebInspector.TextViewerDelegate.prototype.selectionChanged):
+        (WebInspector.TextViewerDelegate.prototype.scrollChanged):
+        (WebInspector.TextEditorChunkedPanel.prototype.scrollToLine):
+
 2012-07-03  Taiju Tsuiki  <tzik@chromium.org>
 
         Web Inspector: Make DirectoryContentView sortable
index e8f02c3..435ae21 100644 (file)
@@ -440,9 +440,9 @@ WebInspector.JavaScriptSourceFrame.prototype = {
         if (this.loaded) {
             this.textEditor.addDecoration(lineNumber, "webkit-execution-line");
             this.revealLine(this._executionLineNumber);
+            if (this.canEditSource())
+                this.setSelection(WebInspector.TextRange.createFromLocation(lineNumber, 0));
         }
-        if (this.canEditSource())
-            this.setSelection(WebInspector.TextRange.createFromLocation(lineNumber, 0));
     },
 
     clearExecutionLine: function()
index 37ac1ef..d939e20 100644 (file)
@@ -75,11 +75,17 @@ WebInspector.SourceFrame.createSearchRegex = function(query)
     return regex;
 }
 
+WebInspector.SourceFrame.Events = {
+    ScrollChanged: "ScrollChanged",
+    SelectionChanged: "SelectionChanged"
+}
+
 WebInspector.SourceFrame.prototype = {
     wasShown: function()
     {
         this._ensureContentLoaded();
         this._textEditor.show(this.element);
+        this._wasShownOrLoaded();
     },
 
     willHide: function()
@@ -146,35 +152,91 @@ WebInspector.SourceFrame.prototype = {
         return this._textModel;
     },
 
+    /**
+     * @param {number} line
+     */
     canHighlightLine: function(line)
     {
         return true;
     },
 
+    /**
+     * @param {number} line
+     */
     highlightLine: function(line)
     {
         this._clearLineToReveal();
-        if (this.loaded)
-            this._textEditor.highlightLine(line);
-        else
-            this._lineToHighlight = line;
+        this._clearLineToScrollTo();
+        this._lineToHighlight = line;
+        this._innerHighlightLineIfNeeded();
+    },
+
+    _innerHighlightLineIfNeeded: function()
+    {
+        if (typeof this._lineToHighlight === "number") {
+            if (this.loaded && this._textEditor.isShowing()) {
+                this._textEditor.highlightLine(this._lineToHighlight);
+                delete this._lineToHighlight
+            }
+        }
     },
 
     _clearLineHighlight: function()
     {
-        if (this.loaded)
-            this._textEditor.clearLineHighlight();
-        else
-            delete this._lineToHighlight;
+        this._textEditor.clearLineHighlight();
+        delete this._lineToHighlight;
     },
 
+    /**
+     * @param {number} line
+     */
     revealLine: function(line)
     {
         this._clearLineHighlight();
-        if (this.loaded)
-            this._textEditor.revealLine(line);
-        else
-            this._lineToReveal = line;
+        this._clearLineToScrollTo();
+        this._lineToReveal = line;
+        this._innerRevealLineIfNeeded();
+    },
+
+    _innerRevealLineIfNeeded: function()
+    {
+        if (typeof this._lineToReveal === "number") {
+            if (this.loaded && this._textEditor.isShowing()) {
+                this._textEditor.revealLine(this._lineToReveal);
+                delete this._lineToReveal
+            }
+        }
+    },
+
+    _clearLineToReveal: function()
+    {
+        delete this._lineToReveal;
+    },
+
+    /**
+     * @param {number} line
+     */
+    scrollToLine: function(line)
+    {
+        this._clearLineHighlight();
+        this._clearLineToReveal();
+        this._lineToScrollTo = line;
+        this._innerScrollToLineIfNeeded();
+    },
+
+    _innerScrollToLineIfNeeded: function()
+    {
+        if (typeof this._lineToScrollTo === "number") {
+            if (this.loaded && this._textEditor.isShowing()) {
+                this._textEditor.scrollToLine(this._lineToScrollTo);
+                delete this._lineToScrollTo
+            }
+        }
+    },
+
+    _clearLineToScrollTo: function()
+    {
+        delete this._lineToScrollTo;
     },
 
     /**
@@ -182,15 +244,24 @@ WebInspector.SourceFrame.prototype = {
      */
     setSelection: function(textRange)
     {
-        if (this.loaded)
-            this._textEditor.setSelection(textRange);
-        else
-            this._selectionToSet = textRange;
+        this._selectionToSet = textRange;
+        this._innerSetSelectionIfNeeded();
     },
 
-    _clearLineToReveal: function()
+    _innerSetSelectionIfNeeded: function()
     {
-        delete this._lineToReveal;
+        if (this._selectionToSet && this.loaded && this._textEditor.isShowing()) {
+            this._textEditor.setSelection(this._selectionToSet);
+            delete this._selectionToSet;
+        }
+    },
+
+    _wasShownOrLoaded: function()
+    {
+        this._innerHighlightLineIfNeeded();
+        this._innerRevealLineIfNeeded();
+        this._innerScrollToLineIfNeeded();
+        this._innerSetSelectionIfNeeded();
     },
 
     beforeTextChanged: function()
@@ -219,20 +290,7 @@ WebInspector.SourceFrame.prototype = {
 
         this._setTextEditorDecorations();
 
-        if (typeof this._lineToHighlight === "number") {
-            this.highlightLine(this._lineToHighlight);
-            delete this._lineToHighlight;
-        }
-
-        if (typeof this._lineToReveal === "number") {
-            this.revealLine(this._lineToReveal);
-            delete this._lineToReveal;
-        }
-
-        if (typeof this._selectionToSet === "object") {
-            this.setSelection(this._selectionToSet);
-            delete this._selectionToSet;
-        }
+        this._wasShownOrLoaded();
 
         if (this._delayedFindSearchMatches) {
             this._delayedFindSearchMatches();
@@ -496,6 +554,22 @@ WebInspector.SourceFrame.prototype = {
      */
     commitEditing: function(text)
     {
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    selectionChanged: function(textRange)
+    {
+        this.dispatchEventToListeners(WebInspector.SourceFrame.Events.SelectionChanged, textRange);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollChanged: function(lineNumber)
+    {
+        this.dispatchEventToListeners(WebInspector.SourceFrame.Events.ScrollChanged, lineNumber);
     }
 }
 
@@ -527,6 +601,22 @@ WebInspector.TextEditorDelegateForSourceFrame.prototype = {
         this._sourceFrame.commitEditing(this._sourceFrame._textModel.text);
     },
 
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    selectionChanged: function(textRange)
+    {
+        this._sourceFrame.selectionChanged(textRange);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollChanged: function(lineNumber)
+    {
+        this._sourceFrame.scrollChanged(lineNumber);
+    },
+
     populateLineGutterContextMenu: function(contextMenu, lineNumber)
     {
         this._sourceFrame.populateLineGutterContextMenu(contextMenu, lineNumber);
index c18e005..31f1a4f 100644 (file)
@@ -62,7 +62,7 @@ WebInspector.TabbedEditorContainer = function(delegate, settingName)
     this._loadedURLs = {};
 
     this._previouslyViewedFilesSetting = WebInspector.settings.createSetting(settingName, []);
-    this._history = new WebInspector.TabbedEditorContainer.History(this._previouslyViewedFilesSetting.get());
+    this._history = WebInspector.TabbedEditorContainer.History.fromObject(this._previouslyViewedFilesSetting.get());
 }
 
 
@@ -108,6 +108,37 @@ WebInspector.TabbedEditorContainer.prototype = {
         this._innerShowFile(uiSourceCode, true);
     },
 
+    _addScrollAndSelectionListeners: function()
+    {
+        console.assert(this._currentFile);
+        var sourceFrame = this._delegate.viewForFile(this._currentFile);
+        sourceFrame.addEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
+        sourceFrame.addEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
+    },
+
+    _removeScrollAndSelectionListeners: function()
+    {
+        if (!this._currentFile)
+            return;
+        var sourceFrame = this._delegate.viewForFile(this._currentFile);
+        sourceFrame.removeEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
+        sourceFrame.removeEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
+    },
+
+    _scrollChanged: function(event)
+    {
+        var lineNumber = /** @type {number} */ event.data;
+        this._history.updateScrollLineNumber(this._currentFile.url, lineNumber);
+        this._history.save(this._previouslyViewedFilesSetting);
+    },
+
+    _selectionChanged: function(event)
+    {
+        var range = /** @type {WebInspector.TextRange} */ event.data;
+        this._history.updateSelectionRange(this._currentFile.url, range);
+        this._history.save(this._previouslyViewedFilesSetting);
+    },
+
     /**
      * @param {WebInspector.UISourceCode} uiSourceCode
      * @param {boolean=} userGesture
@@ -116,6 +147,7 @@ WebInspector.TabbedEditorContainer.prototype = {
     {
         if (this._currentFile === uiSourceCode)
             return;
+        this._removeScrollAndSelectionListeners();
         this._currentFile = uiSourceCode;
 
         var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, userGesture);
@@ -124,6 +156,8 @@ WebInspector.TabbedEditorContainer.prototype = {
         if (userGesture)
             this._editorSelectedByUserAction();
         
+        this._addScrollAndSelectionListeners();
+        
         this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._currentFile);
     },
 
@@ -225,6 +259,13 @@ WebInspector.TabbedEditorContainer.prototype = {
         this._tabIds.put(uiSourceCode, tabId);
         this._files[tabId] = uiSourceCode;
 
+        var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.url);
+        if (savedScrollLineNumber)
+            view.scrollToLine(savedScrollLineNumber);
+        var savedSelectionRange = this._history.selectionRange(uiSourceCode.url);
+        if (savedSelectionRange)
+            view.setSelection(savedSelectionRange);
+
         this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture);
 
         uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
@@ -252,6 +293,7 @@ WebInspector.TabbedEditorContainer.prototype = {
         var tabId = /** @type {string} */ event.data.tabId;
         var userGesture = /** @type {boolean} */ event.data.isUserGesture;
 
+        this._removeScrollAndSelectionListeners();
         var uiSourceCode = this._files[tabId];
         this._tabIds.remove(uiSourceCode);
         delete this._files[tabId];
@@ -361,19 +403,122 @@ WebInspector.TabbedEditorContainer.prototype.__proto__ = WebInspector.Object.pro
 
 /**
  * @constructor
+ * @param {string} url
+ * @param {WebInspector.TextRange=} selectionRange
+ * @param {number=} scrollLineNumber
+ */
+WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, scrollLineNumber)
+{
+    this.url = url;
+    this.selectionRange = selectionRange;
+    this.scrollLineNumber = scrollLineNumber;
+}
+
+/**
+ * @param {Object} serializedHistoryItem
+ * @return {WebInspector.TabbedEditorContainer.HistoryItem}
+ */
+WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function (serializedHistoryItem)
+{
+    var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange) : null;
+    return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber);
+}
+
+WebInspector.TabbedEditorContainer.HistoryItem.prototype = {
+    /**
+     * @return {Object}
+     */
+    serializeToObject: function()
+    {
+        var serializedHistoryItem = {};
+        serializedHistoryItem.url = this.url;
+        serializedHistoryItem.selectionRange = this.selectionRange;
+        serializedHistoryItem.scrollLineNumber = this.scrollLineNumber;
+        return serializedHistoryItem;
+    }
+}
+
+WebInspector.TabbedEditorContainer.HistoryItem.prototype.__proto__ = WebInspector.Object.prototype;
+
+/**
+ * @constructor
+ * @param {Array.<WebInspector.TabbedEditorContainer.HistoryItem>} items
  */
-WebInspector.TabbedEditorContainer.History = function(urls)
+WebInspector.TabbedEditorContainer.History = function(items)
 {
-    this._urls = urls;
+    this._items = items;
+}
+
+/**
+ * @param {Object} serializedHistory
+ * @return {WebInspector.TabbedEditorContainer.History}
+ */
+WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHistory)
+{
+    var items = [];
+    for (var i = 0; i < serializedHistory.length; ++i)
+        items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i]));
+    return new WebInspector.TabbedEditorContainer.History(items);
 }
 
 WebInspector.TabbedEditorContainer.History.prototype = {
     /**
      * @param {string} url
+     * @return {number}
      */
     index: function(url)
     {
-        return this._urls.indexOf(url);
+        for (var i = 0; i < this._items.length; ++i) {
+            if (this._items[i].url === url)
+                return i;
+        }
+        return -1;
+    },
+
+    /**
+     * @param {string} url
+     * @return {WebInspector.TextRange|undefined}
+     */
+    selectionRange: function(url)
+    {
+        var index = this.index(url);
+        return index !== -1 ? this._items[index].selectionRange : undefined;
+    },
+
+    /**
+     * @param {string} url
+     * @param {WebInspector.TextRange} selectionRange
+     */
+    updateSelectionRange: function(url, selectionRange)
+    {
+        if (!selectionRange)
+            return;
+        var index = this.index(url);
+        if (index === -1)
+            return;
+        this._items[index].selectionRange = selectionRange;
+    },
+
+    /**
+     * @param {string} url
+     * @return {number|undefined}
+     */
+    scrollLineNumber: function(url)
+    {
+        var index = this.index(url);
+        return index !== -1 ? this._items[index].scrollLineNumber : undefined;
+    },
+
+    /**
+     * @param {string} url
+     * @param {number} scrollLineNumber
+     */
+    updateScrollLineNumber: function(url, scrollLineNumber)
+    {
+        var index = this.index(url);
+        if (index === -1)
+            return;
+        this._items[index].scrollLineNumber = scrollLineNumber;
     },
 
     /**
@@ -382,10 +527,14 @@ WebInspector.TabbedEditorContainer.History.prototype = {
     update: function(urls)
     {
         for (var i = urls.length - 1; i >= 0; --i) {
-            var index = this._urls.indexOf(urls[i]);
-            if (index !== -1)
-                this._urls.splice(index, 1);
-            this._urls.unshift(urls[i]);
+            var index = this.index(urls[i]);
+            var item;
+            if (index !== -1) {
+                item = this._items[index];
+                this._items.splice(index, 1);
+            } else
+                item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]);
+            this._items.unshift(item);
         }
     },
 
@@ -394,9 +543,9 @@ WebInspector.TabbedEditorContainer.History.prototype = {
      */
     remove: function(url)
     {
-        var index = this._urls.indexOf(url);
+        var index = this.index(url);
         if (index !== -1)
-            this._urls.splice(index, 1);
+            this._items.splice(index, 1);
     },
     
     /**
@@ -404,7 +553,18 @@ WebInspector.TabbedEditorContainer.History.prototype = {
      */
     save: function(setting)
     {
-        setting.set(this._urls);
+        setting.set(this._serializeToObject());
+    },
+    
+    /**
+     * @return {Object}
+     */
+    _serializeToObject: function()
+    {
+        var serializedHistory = [];
+        for (var i = 0; i < this._items.length; ++i)
+            serializedHistory.push(this._items[i].serializeToObject());
+        return serializedHistory;
     }
 }
 
index 88314e6..00e2380 100644 (file)
@@ -55,6 +55,10 @@ WebInspector.TextEditor = function(textModel, url, delegate)
     var syncLineHeightListener = this._syncLineHeight.bind(this);
     this._mainPanel = new WebInspector.TextEditorMainPanel(this._textModel, url, syncScrollListener, syncDecorationsForLineListener, enterTextChangeMode, exitTextChangeMode);
     this._gutterPanel = new WebInspector.TextEditorGutterPanel(this._textModel, syncDecorationsForLineListener, syncLineHeightListener);
+
+    this._mainPanel.element.addEventListener("scroll", this._handleScrollChanged.bind(this), false);
+    this._mainPanel._container.addEventListener("focus", this._handleFocused.bind(this), false);
+
     this.element.appendChild(this._mainPanel.element);
     this.element.appendChild(this._gutterPanel.element);
 
@@ -355,6 +359,29 @@ WebInspector.TextEditor.prototype = {
         return true;
     },
 
+    _handleScrollChanged: function(event)
+    {
+        var visibleFrom = this._mainPanel.element.scrollTop;
+        var firstVisibleLineNumber = this._mainPanel._findFirstVisibleLineNumber(visibleFrom);
+        this._delegate.scrollChanged(firstVisibleLineNumber);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollToLine: function(lineNumber)
+    {
+        this._mainPanel.scrollToLine(lineNumber);
+    },
+
+    _handleSelectionChange: function(event)
+    {
+        var textRange = this._mainPanel._getSelection();
+        if (textRange)
+            this._lastSelection = textRange;
+        this._delegate.selectionChanged(textRange);
+    },
+
     /**
      * @return {WebInspector.TextRange}
      */
@@ -368,6 +395,7 @@ WebInspector.TextEditor.prototype = {
      */
     setSelection: function(textRange)
     {
+        this._lastSelection = textRange;
         this._mainPanel._restoreSelection(textRange);
     },
 
@@ -375,10 +403,26 @@ WebInspector.TextEditor.prototype = {
     {
         if (!this.readOnly())
             WebInspector.markBeingEdited(this.element, true);
+
+        this._boundSelectionChangeListener = this._handleSelectionChange.bind(this);
+        document.addEventListener("selectionchange", this._boundSelectionChangeListener, false);
+    },
+
+    _handleFocused: function()
+    {
+        if (this._lastSelection) {
+            // We do not restore selection after focus lost to avoid selection blinking. We restore only cursor position instead.
+            // FIXME: consider adding selection decoration to blurred editor.
+            var newSelection = WebInspector.TextRange.createFromLocation(this._lastSelection.endLine, this._lastSelection.endColumn);
+            this.setSelection(newSelection);
+        }
     },
 
     willHide: function()
     {
+        document.removeEventListener("selectionchange", this._boundSelectionChangeListener, false);
+        delete this._boundSelectionChangeListener;
+
         if (!this.readOnly())
             WebInspector.markBeingEdited(this.element, false);
     }
@@ -405,6 +449,16 @@ WebInspector.TextEditorDelegate.prototype = {
     commitEditing: function() { },
 
     /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    selectionChanged: function(textRange) { },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollChanged: function(lineNumber) { },
+
+    /**
      * @param {WebInspector.ContextMenu} contextMenu
      * @param {number} lineNumber
      */
@@ -442,6 +496,18 @@ WebInspector.TextEditorChunkedPanel.prototype = {
     /**
      * @param {number} lineNumber
      */
+    scrollToLine: function(lineNumber)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var chunk = this.makeLineAChunk(lineNumber);
+        this.element.scrollTop = chunk.offsetTop;
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
     revealLine: function(lineNumber)
     {
         if (lineNumber >= this._textModel.linesCount)
index c86adb5..c237a66 100644 (file)
@@ -48,6 +48,15 @@ WebInspector.TextRange.createFromLocation = function(line, column)
     return new WebInspector.TextRange(line, column, line, column);
 }
 
+/**
+ * @param {Object} serializedTextRange
+ * @return {WebInspector.TextRange}
+ */
+WebInspector.TextRange.fromObject = function (serializedTextRange)
+{
+    return new WebInspector.TextRange(serializedTextRange.startLine, serializedTextRange.startColumn, serializedTextRange.endLine, serializedTextRange.endColumn);
+}
+
 WebInspector.TextRange.prototype = {
     /**
      * @return {boolean}
@@ -87,6 +96,19 @@ WebInspector.TextRange.prototype = {
     clone: function()
     {
         return new WebInspector.TextRange(this.startLine, this.startColumn, this.endLine, this.endColumn);
+    },
+
+    /**
+     * @return {Object}
+     */
+    serializeToObject: function()
+    {
+        var serializedTextRange = {};
+        serializedTextRange.startLine = this.startLine;
+        serializedTextRange.startColumn = this.startColumn;
+        serializedTextRange.endLine = this.endLine;
+        serializedTextRange.endColumn = this.endColumn;
+        return serializedTextRange;
     }
 }