diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index ed35555196..33fac29f32 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -10,13 +10,14 @@ define [ "ide/editor/directives/aceEditor/spell-check/SpellCheckAdapter" "ide/editor/directives/aceEditor/highlights/HighlightsManager" "ide/editor/directives/aceEditor/cursor-position/CursorPositionManager" + "ide/editor/directives/aceEditor/cursor-position/CursorPositionAdapter" "ide/editor/directives/aceEditor/track-changes/TrackChangesManager" "ide/editor/directives/aceEditor/metadata/MetadataManager" "ide/metadata/services/metadata" "ide/graphics/services/graphics" "ide/preamble/services/preamble" - "ide/files/services/files" -], (App, Ace, SearchBox, Vim, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, SpellCheckAdapter, HighlightsManager, CursorPositionManager, TrackChangesManager, MetadataManager) -> + "ide/files/services/files" +], (App, Ace, SearchBox, Vim, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, SpellCheckAdapter, HighlightsManager, CursorPositionManager, CursorPositionAdapter, TrackChangesManager, MetadataManager) -> EditSession = ace.require('ace/edit_session').EditSession ModeList = ace.require('ace/ext/modelist') Vim = ace.require('ace/keyboard/vim').Vim @@ -108,7 +109,7 @@ define [ undoManager = new UndoManager(scope, editor, element) highlightsManager = new HighlightsManager(scope, editor, element) - cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage) + cursorPositionManager = new CursorPositionManager(scope, new CursorPositionAdapter(editor), localStorage) trackChangesManager = new TrackChangesManager(scope, editor, element) metadataManager = new MetadataManager(scope, editor, element, metadata) autoCompleteManager = new AutoCompleteManager(scope, editor, element, metadataManager, graphics, preamble, files) @@ -308,10 +309,12 @@ define [ scope.$watch "sharejsDoc", (sharejs_doc, old_sharejs_doc) -> if old_sharejs_doc? + scope.$broadcast('beforeChangeDocument') detachFromAce(old_sharejs_doc) - if sharejs_doc? attachToAce(sharejs_doc) + if sharejs_doc? and old_sharejs_doc? + scope.$broadcast('afterChangeDocument') scope.$watch "text", (text) -> if text? @@ -380,6 +383,30 @@ define [ editor.off 'changeSession', onSessionChangeForSpellCheck editor.off 'nativecontextmenu', spellCheckManager.onContextMenu + onSessionChangeForCursorPosition = (e) -> + e.oldSession?.selection.off 'changeCursor', cursorPositionManager.onCursorChange + e.session.selection.on 'changeCursor', cursorPositionManager.onCursorChange + + onUnloadForCursorPosition = () -> + cursorPositionManager.onUnload(editor.getSession()) + + initCursorPosition = () -> + editor.on 'changeSession', onSessionChangeForCursorPosition + onSessionChangeForCursorPosition({ session: editor.getSession() }) # Force initial setup + $(window).on "unload", onUnloadForCursorPosition + + tearDownCursorPosition = () -> + editor.off 'changeSession', onSessionChangeForCursorPosition + $(window).off "unload", onUnloadForCursorPosition + + initCursorPosition() + + # Trigger the event once *only* - this is called after Ace is connected + # to the ShareJs instance but this event should only be triggered the + # first time the editor is opened. Not every time the docs opened + triggerEditorInitEvent = _.once () -> + scope.$broadcast('editorInit') + attachToAce = (sharejs_doc) -> lines = sharejs_doc.getSnapshot().split("\n") session = editor.getSession() @@ -425,6 +452,7 @@ define [ editor.initing = false # now ready to edit document editor.setReadOnly(scope.readOnly) # respect the readOnly setting, normally false + triggerEditorInitEvent() initSpellCheck() resetScrollMargins() @@ -489,6 +517,7 @@ define [ scope.$on '$destroy', () -> if scope.sharejsDoc? tearDownSpellCheck() + tearDownCursorPosition() detachFromAce(scope.sharejsDoc) session = editor.getSession() session?.destroy() diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionAdapter.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionAdapter.coffee new file mode 100644 index 0000000000..d58f012b57 --- /dev/null +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionAdapter.coffee @@ -0,0 +1,32 @@ +define [ + "ide/editor/AceShareJsCodec" +], (AceShareJsCodec) -> + class CursorPositionAdapter + constructor: (@editor) -> + + getCursor: () -> + @editor.getCursorPosition() + + getEditorScrollPosition: () -> + @editor.getFirstVisibleRow() + + setCursor: (pos) -> + pos = pos.cursorPosition or { row: 0, column: 0 } + @editor.moveCursorToPosition(pos) + + setEditorScrollPosition: (pos) -> + pos = pos.firstVisibleLine or 0 + @editor.scrollToLine(pos) + + clearSelection: () -> + @editor.selection.clearSelection() + + gotoLine: (line, column) -> + @editor.gotoLine(line, column) + @editor.scrollToLine(line, true, true) # centre and animate + @editor.focus() + + gotoOffset: (offset) -> + lines = @editor.getSession().getDocument().getAllLines() + position = AceShareJsCodec.shareJsOffsetToAcePosition(offset, lines) + @gotoLine(position.row + 1, position.column) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee index 119aa471e1..2cf025f70e 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee @@ -1,75 +1,60 @@ -define [ - "ide/editor/AceShareJsCodec" -], (AceShareJsCodec) -> +define [], () -> class CursorPositionManager - constructor: (@$scope, @editor, @element, @localStorage) -> + constructor: (@$scope, @adapter, @localStorage) -> + @$scope.$on 'editorInit', @jumpToPositionInNewDoc - onChangeCursor = (e) => - @emitCursorUpdateEvent(e) + @$scope.$on 'beforeChangeDocument', () => + @storeCursorPosition() + @storeFirstVisibleLine() - @editor.on "changeSession", (e) => - if e.oldSession? - @storeCursorPosition(e.oldSession) - @storeScrollTopPosition(e.oldSession) - - @doc_id = @$scope.sharejsDoc?.doc_id - - e.oldSession?.selection.off 'changeCursor', onChangeCursor - e.session.selection.on 'changeCursor', onChangeCursor - - setTimeout () => - @gotoStoredPosition() - , 0 - - $(window).on "unload", () => - @storeCursorPosition(@editor.getSession()) - @storeScrollTopPosition(@editor.getSession()) + @$scope.$on 'afterChangeDocument', @jumpToPositionInNewDoc @$scope.$on "#{@$scope.name}:gotoLine", (e, line, column) => if line? setTimeout () => - @gotoLine(line, column) + @adapter.gotoLine(line, column) , 10 # Hack: Must happen after @gotoStoredPosition @$scope.$on "#{@$scope.name}:gotoOffset", (e, offset) => if offset? setTimeout () => - @gotoOffset(offset) + @adapter.gotoOffset(offset) , 10 # Hack: Must happen after @gotoStoredPosition @$scope.$on "#{@$scope.name}:clearSelection", (e) => - @editor.selection.clearSelection() + @adapter.clearSelection() - storeScrollTopPosition: (session) -> + jumpToPositionInNewDoc: () => + @doc_id = @$scope.sharejsDoc?.doc_id + setTimeout () => + @gotoStoredPosition() + , 0 + + onUnload: () => + @storeCursorPosition() + @storeFirstVisibleLine() + + onCursorChange: () => + @emitCursorUpdateEvent() + + storeFirstVisibleLine: () -> if @doc_id? docPosition = @localStorage("doc.position.#{@doc_id}") || {} - docPosition.scrollTop = session.getScrollTop() + docPosition.firstVisibleLine = @adapter.getEditorScrollPosition() @localStorage("doc.position.#{@doc_id}", docPosition) - storeCursorPosition: (session) -> + storeCursorPosition: () -> if @doc_id? docPosition = @localStorage("doc.position.#{@doc_id}") || {} - docPosition.cursorPosition = session.selection.getCursor() + docPosition.cursorPosition = @adapter.getCursor() @localStorage("doc.position.#{@doc_id}", docPosition) - + emitCursorUpdateEvent: () -> - cursor = @editor.getCursorPosition() + cursor = @adapter.getCursor() @$scope.$emit "cursor:#{@$scope.name}:update", cursor gotoStoredPosition: () -> return if !@doc_id? pos = @localStorage("doc.position.#{@doc_id}") || {} - @ignoreCursorPositionChanges = true - @editor.moveCursorToPosition(pos.cursorPosition or {row: 0, column: 0}) - @editor.getSession().setScrollTop(pos.scrollTop or 0) - delete @ignoreCursorPositionChanges - - gotoLine: (line, column) -> - @editor.gotoLine(line, column) - @editor.scrollToLine(line,true,true) # centre and animate - @editor.focus() - - gotoOffset: (offset) -> - lines = @editor.getSession().getDocument().getAllLines() - position = AceShareJsCodec.shareJsOffsetToAcePosition(offset, lines) - @gotoLine(position.row + 1, position.column) \ No newline at end of file + @adapter.setCursor(pos) + @adapter.setEditorScrollPosition(pos)