Просмотр исходного кода

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

Abstract cursor position tracking so that it is not bound to Ace
Timothée Alby 8 лет назад
Родитель
Сommit
05bc4cc9e9

+ 33 - 4
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()

+ 32 - 0
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)

+ 31 - 46
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()
+
+		jumpToPositionInNewDoc: () =>
+			@doc_id = @$scope.sharejsDoc?.doc_id
+			setTimeout () =>
+				@gotoStoredPosition()
+			, 0
+
+		onUnload: () =>
+			@storeCursorPosition()
+			@storeFirstVisibleLine()
 
-		storeScrollTopPosition: (session) ->
+		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)