codecombat/app/views/play/level/tome/Problem.coffee

103 lines
4.5 KiB
CoffeeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Range = ace.require('ace/range').Range
# This class can either wrap an AetherProblem,
# or act as a general runtime error container for web-dev iFrame errors.
# TODO: Use subclasses? Might need a factory pattern for that (bleh)
module.exports = class Problem
annotation: null
markerRange: null
# Construction with AetherProblem will include all but `error`
# Construction with a standard error will have `error`, `isCast`, `levelID`, `ace`
constructor: ({ @aether, @aetherProblem, @ace, isCast=false, @levelID, error, userCodeHasChangedSinceLastCast }) ->
if @aetherProblem
@annotation = @buildAnnotationFromAetherProblem(@aetherProblem)
{ @lineMarkerRange, @textMarkerRange } = @buildMarkerRangesFromAetherProblem(@aetherProblem) if isCast
{ @level, @range, @message, @hint, @userInfo } = @aetherProblem
{ @row, @column: col } = @aetherProblem.range?[0] or {}
@createdBy = 'aether'
else
unless userCodeHasChangedSinceLastCast
@annotation = @buildAnnotationFromWebDevError(error)
{ @lineMarkerRange, @textMarkerRange } = @buildMarkerRangesFromWebDevError(error)
@level = 'error'
@row = error.line
@column = error.column
@message = error.message or 'Unknown Error'
if error.line and not userCodeHasChangedSinceLastCast
@message = "Line #{error.line + 1}: " + @message # Ace's gutter numbers are 1-indexed but annotation.rows are 0-indexed
if userCodeHasChangedSinceLastCast
@hint = "This error was generated by old code — Try running your new code first."
else
@hint = undefined
@userInfo = undefined
@createdBy = 'web-dev-iframe'
# TODO: Include runtime/transpile error types depending on something?
# TODO: get ACE screen line, too, for positioning, since any multiline "lines" will mess up positioning
Backbone.Mediator.publish("problem:problem-created", line: @annotation.row, text: @annotation.text) if application.isIPadApp
isEqual: (problem) ->
_.all ['row', 'column', 'level', 'column', 'message', 'hint'], (attr) =>
@[attr] is problem[attr]
destroy: ->
@removeMarkerRanges()
@userCodeProblem.off() if @userCodeProblem
buildAnnotationFromWebDevError: (error) ->
{
row: error.line
column: error.column
raw: error.message
text: error.message
type: 'error'
createdBy: 'web-dev-iframe'
}
buildAnnotationFromAetherProblem: (aetherProblem) ->
return unless aetherProblem.range
text = aetherProblem.message.replace /^Line \d+: /, ''
start = aetherProblem.range[0]
{
row: start.row,
column: start.col,
raw: text,
text: text,
type: @aetherProblem.level ? 'error'
createdBy: 'aether'
}
buildMarkerRangesFromWebDevError: (error) ->
lineMarkerRange = new Range error.line, 0, error.line, 1
lineMarkerRange.start = @ace.getSession().getDocument().createAnchor lineMarkerRange.start
lineMarkerRange.end = @ace.getSession().getDocument().createAnchor lineMarkerRange.end
lineMarkerRange.id = @ace.getSession().addMarker lineMarkerRange, 'problem-line', 'fullLine'
textMarkerRange = undefined # We don't get any per-character info from standard errors
{ lineMarkerRange, textMarkerRange }
buildMarkerRangesFromAetherProblem: (aetherProblem) ->
return {} unless aetherProblem.range
[start, end] = aetherProblem.range
textClazz = "problem-marker-#{aetherProblem.level}"
textMarkerRange = new Range start.row, start.col, end.row, end.col
textMarkerRange.start = @ace.getSession().getDocument().createAnchor textMarkerRange.start
textMarkerRange.end = @ace.getSession().getDocument().createAnchor textMarkerRange.end
textMarkerRange.id = @ace.getSession().addMarker textMarkerRange, textClazz, 'text'
lineClazz = "problem-line"
lineMarkerRange = new Range start.row, start.col, end.row, end.col
lineMarkerRange.start = @ace.getSession().getDocument().createAnchor lineMarkerRange.start
lineMarkerRange.end = @ace.getSession().getDocument().createAnchor lineMarkerRange.end
lineMarkerRange.id = @ace.getSession().addMarker lineMarkerRange, lineClazz, 'fullLine'
{ lineMarkerRange, textMarkerRange }
removeMarkerRanges: ->
if @textMarkerRange
@ace.getSession().removeMarker @textMarkerRange.id
@textMarkerRange.start.detach()
@textMarkerRange.end.detach()
if @lineMarkerRange
@ace.getSession().removeMarker @lineMarkerRange.id
@lineMarkerRange.start.detach()
@lineMarkerRange.end.detach()