Merge pull request #729 from sharelatex/as-cursor-position

Abstract cursor position tracking so that it is not bound to Ace
This commit is contained in:
Timothée Alby 2018-07-11 10:21:42 +02:00 committed by GitHub
commit 05bc4cc9e9
3 changed files with 96 additions and 50 deletions

View file

@ -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()

View file

@ -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)

View file

@ -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)
@adapter.setCursor(pos)
@adapter.setEditorScrollPosition(pos)