Refactor auto-compile trigger logic

This commit is contained in:
James Allen 2018-02-08 13:32:15 +00:00
parent 5bf2ff0a47
commit 7866a91d21

View file

@ -5,8 +5,12 @@ define [
"libs/bib-log-parser"
"services/log-hints-feedback"
], (App, Ace, HumanReadableLogs, BibLogParser) ->
AUTO_COMPILE_TIMEOUT = 5000
OP_ACKNOWLEDGEMENT_TIMEOUT = 1100
AUTO_COMPILE_MAX_WAIT = 5000
# We add a 1 second debounce to sending user changes to server if they aren't
# collaborating with anyone. This needs to be higher than that, and allow for
# client to server latency, otherwise we compile before the op reaches the server
# and then again on ack.
AUTO_COMPILE_DEBOUNCE = 2000
App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, logHintsFeedback, localStorage) ->
# enable per-user containers by default
@ -75,59 +79,96 @@ define [
$scope.pdf.view = 'errors'
$scope.pdf.renderingError = true
autoCompileTimeout = null
autoCompileInterval = null
autoCompileIfReady = () ->
if $scope.pdf.compiling
return
# Only checking linting if syntaxValidation is on and visible to the user
autoCompileLintingError = ide.$scope.hasLintingError and ide.$scope.settings.syntaxValidation
if $scope.autoCompileLintingError != autoCompileLintingError
$scope.$apply () ->
$scope.autoCompileLintingError = autoCompileLintingError
# We've likely been waiting a while until the user fixed the linting, but we
# don't want to compile as soon as it is fixed, so reset the timeout.
$scope.startedTryingAutoCompileAt = Date.now()
$scope.docLastChangedAt = Date.now()
if autoCompileLintingError
return
# If there's a longish compile, don't compile immediately after if user is still typing
startedTryingAt = Math.max($scope.startedTryingAutoCompileAt, $scope.lastFinishedCompileAt || 0)
timeSinceStartedTrying = Date.now() - startedTryingAt
timeSinceLastChange = Date.now() - $scope.docLastChangedAt
shouldCompile = false
if timeSinceLastChange > AUTO_COMPILE_DEBOUNCE # Don't compile in the middle of the user typing
shouldCompile = true
else if timeSinceStartedTrying > AUTO_COMPILE_MAX_WAIT # Unless they type for a long time
shouldCompile = true
else if timeSinceStartedTrying < 0 or timeSinceLastChange < 0
# If time is non-monotonic, assume that the user's system clock has been
# changed and continue with compile
shouldCompile = true
if shouldCompile
triggerAutoCompile()
triggerAutoCompile = () ->
return if autoCompileTimeout or $scope.ui.pdfHidden
$scope.recompile(isAutoCompileOnChange: true)
timeSinceLastCompile = Date.now() - $scope.recompiledAt
# If time is non-monotonic, assume that the user's system clock has been
# changed and continue with recompile
isTimeNonMonotonic = timeSinceLastCompile < 0
startTryingAutoCompile = () ->
return if autoCompileInterval?
$scope.startedTryingAutoCompileAt = Date.now()
autoCompileInterval = setInterval autoCompileIfReady, 200
if isTimeNonMonotonic || timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT
# If user has code check disabled, it is likely because they have
# linting errors that they are ignoring. Therefore it doesn't make sense
# to block auto compiles. It also causes problems where server-provided
# linting errors aren't cleared after typing
if (ide.$scope.settings.syntaxValidation and !ide.$scope.hasLintingError)
$scope.recompile(isAutoCompileOnChange: true) # compile if no linting errors
else if !ide.$scope.settings.syntaxValidation
$scope.recompile(isAutoCompileOnChange: true) # always recompile
else
$scope.$apply () ->
$scope.autoCompileLintingError = true
stopTryingAutoCompile = () ->
clearInterval autoCompileInterval
autoCompileInterval = null
$scope.$watch "uncompiledChanges", (uncompiledChanges) ->
if uncompiledChanges
startTryingAutoCompile()
else
# Extend remainder of timeout
autoCompileTimeout = setTimeout () ->
autoCompileTimeout = null
triggerAutoCompile()
, AUTO_COMPILE_TIMEOUT - timeSinceLastCompile
stopTryingAutoCompile()
$scope.uncompiledChanges = false
recalculateUncompiledChanges = () ->
if $scope.docLastChanged > $scope.projectLastCompiled
if $scope.ui.pdfHidden
# Don't both auto-compiling if pdf isn't visible
$scope.uncompiledChanges = false
else if !$scope.docLastChangedAt?
$scope.uncompiledChanges = false
else if !$scope.lastStartedCompileAt? or $scope.docLastChangedAt > $scope.lastStartedCompileAt
$scope.uncompiledChanges = true
else
$scope.uncompiledChanges = false
onDocChanged = () ->
$scope.autoCompileLintingError = false
$scope.docLastChanged = Date.now()
_updateDocLastChangedAt = () ->
$scope.docLastChangedAt = Date.now()
recalculateUncompiledChanges()
onDocChanged = () ->
$scope.autoCompileLintingError = false
_updateDocLastChangedAt()
onDocSaved = () ->
# We use the save as a trigger too, to account for the delay between the client
# and server. Otherwise, we might have compiled after the user made
# the change on the client, but before the server had it.
_updateDocLastChangedAt()
onCompilingStateChanged = (compiling) ->
if compiling
$scope.projectLastCompiled = Date.now()
recalculateUncompiledChanges()
recalculateUncompiledChanges()
autoCompileListeners = []
toggleAutoCompile = (enabling) ->
if enabling
autoCompileListeners = [
ide.$scope.$on "doc:changed", onDocChanged
ide.$scope.$on "doc:saved", onDocChanged
ide.$scope.$on "doc:saved", onDocSaved
$scope.$watch "pdf.compiling", onCompilingStateChanged
ide.$scope.$on "ide:opAcknowledged", _.debounce(triggerAutoCompile, OP_ACKNOWLEDGEMENT_TIMEOUT)
]
else
for unbind in autoCompileListeners
@ -455,6 +496,7 @@ define [
event_tracking.sendMBSampled "editor-recompile-sampled", options
$scope.lastStartedCompileAt = Date.now()
$scope.pdf.compiling = true
$scope.pdf.isAutoCompileOnLoad = options?.isAutoCompileOnLoad # initial autocompile
@ -472,6 +514,8 @@ define [
options.rootDocOverride_id = getRootDocOverride_id()
$scope.$apply()
sendCompileRequest(options)
.then (response) ->
{ data } = response
@ -487,7 +531,7 @@ define [
$scope.pdf.error = true
$scope.pdf.view = 'errors'
.finally () ->
$scope.recompiledAt = Date.now()
$scope.lastFinishedCompileAt = Date.now()
# This needs to be public.
ide.$scope.recompile = $scope.recompile