From ffeb1a12491780d489d96dd7159a09b5fd16090c Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Fri, 18 Sep 2015 16:24:32 +0200 Subject: Add "Plan" button to display next "planning" hint --- js/codeq/hint.js | 13 ++++++++++++- js/codeq/prolog.js | 15 +++++++++++++-- js/codeq/python.js | 13 ++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) (limited to 'js') diff --git a/js/codeq/hint.js b/js/codeq/hint.js index 06eceb0..c06aef7 100644 --- a/js/codeq/hint.js +++ b/js/codeq/hint.js @@ -7,9 +7,10 @@ var firstCharacterPos = {'line': 0, 'ch': 0}, sel_no_scroll = {'scroll': false}; - codeq.makeHinter = function (jqHints, jqEditor, editor, hintDefs) { + codeq.makeHinter = function (jqHints, jqEditor, editor, hintDefs, planDef) { var hintCounter = 0, // for generating unique class-names hintCleaners = [], + planIdx = 0, clearHints = function () { var i; @@ -124,6 +125,16 @@ }; return { + /** Display the next "planning" hint and return whether there are + * any more available. + */ + 'planNext': function () { + if (planIdx < planDef.length) { + jqHints.append('
' + planDef[planIdx++] + '
'); + } + return planIdx < planDef.length; + }, + /** * Processes and display appropriately the server hints. * TODO: sort hints so static and popup hints come first, and a (single) drop-down hint last diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js index 96c3077..4eed354 100644 --- a/js/codeq/prolog.js +++ b/js/codeq/prolog.js @@ -95,9 +95,11 @@ * add the above function to the buttons */ addClickListenerTranstions = function(){ + $('#btn_code_plan').on('click',clickListenerTransitionFun); $('#btn_code_hint').on('click',clickListenerTransitionFun); $('#btn_code_test').on('click',clickListenerTransitionFun); + $('#btn_code_plan').on(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_hint').on(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_test').on(transitionEventName,mouseDownEventIgnoreFun); }, @@ -105,9 +107,11 @@ * and a function to remove it */ removeClickListenerTransition = function(){ + $('#btn_code_plan').off('click',clickListenerTransitionFun); $('#btn_code_hint').off('click',clickListenerTransitionFun); $('#btn_code_test').off('click',clickListenerTransitionFun); + $('#btn_code_plan').off(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_hint').off(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_test').off(transitionEventName,mouseDownEventIgnoreFun); }, @@ -362,11 +366,12 @@ editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true }), activityHandler = makeActivityHandler(editor, problem.id), terminal = makePrologTerminalHandler(jqConsole, editor, problem.id, activityHandler), - hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint); + hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint, problem.plan); editor.setValue(info.solution); $('#title').text(problem.slug); jqDescription.html(problem.description); + $('#btn_code_plan').prop('disabled', problem.plan.length == 0); editor.on('change', function (instance, changeObj) { var doc = editor.getDoc(), @@ -381,6 +386,12 @@ } }); + $('#btn_code_plan').on('click', function () { + if (!hinter.planNext()) { + $('#btn_code_plan').prop('disabled', true); + $('#btn_code_plan').blur(); + } + }); $('#btn_code_hint').on('click', function () { terminal.append('hint.\n', 'input'); terminal.inputDisable(); @@ -451,4 +462,4 @@ } }; }; -})(); \ No newline at end of file +})(); diff --git a/js/codeq/python.js b/js/codeq/python.js index e856c08..1d4ee17 100644 --- a/js/codeq/python.js +++ b/js/codeq/python.js @@ -97,9 +97,11 @@ * add the above function to the buttons */ addClickListenerTranstions = function(){ + $('#btn_code_plan').on('click',clickListenerTransitionFun); $('#btn_code_hint').on('click',clickListenerTransitionFun); $('#btn_code_test').on('click',clickListenerTransitionFun); + $('#btn_code_plan').on(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_hint').on(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_test').on(transitionEventName,mouseDownEventIgnoreFun); }, @@ -107,9 +109,11 @@ * and a function to remove it */ removeClickListenerTransition = function(){ + $('#btn_code_plan').off('click',clickListenerTransitionFun); $('#btn_code_hint').off('click',clickListenerTransitionFun); $('#btn_code_test').off('click',clickListenerTransitionFun); + $('#btn_code_plan').off(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_hint').off(transitionEventName,mouseDownEventIgnoreFun); $('#btn_code_test').off(transitionEventName,mouseDownEventIgnoreFun); }, @@ -312,11 +316,12 @@ editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true, mode: 'python' }), activityHandler = makeActivityHandler(editor, problem.id), terminal = makePythonTerminalHandler(jqConsole, editor, problem.id, activityHandler), - hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint); + hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint, problem.plan); editor.setValue(info.solution); $('#title').text(problem.slug); jqDescription.html(problem.description); + $('#btn_code_plan').prop('disabled', problem.plan.length == 0); editor.on('change', function (instance, changeObj) { var doc = editor.getDoc(), @@ -331,6 +336,12 @@ } }); + $('#btn_code_plan').on('click', function () { + if (!hinter.planNext()) { + $('#btn_code_plan').prop('disabled', true); + $('#btn_code_plan').blur(); + } + }); $('#btn_code_hint').on('click', function () { var doc = editor.getDoc(); codeq.comms.sendHint({ -- cgit v1.2.1 From ea3d8109b4f90e83e5581f9434c828785c08d932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Fri, 18 Sep 2015 16:33:46 +0200 Subject: Log errors to console. --- js/codeq/language.js | 3 ++- js/codeq/login.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'js') diff --git a/js/codeq/language.js b/js/codeq/language.js index 78fa83a..1a87097 100644 --- a/js/codeq/language.js +++ b/js/codeq/language.js @@ -71,7 +71,8 @@ }) .fail(function (reason) { $('#disabled').css('display', 'none'); - alert('Login request failed: ' + reason); + codeq.log.error('Getting problem definitions failed: ' + reason, reason); + alert('Getting problem definitions failed: ' + reason); }) .done(); } diff --git a/js/codeq/login.js b/js/codeq/login.js index b09bf1e..11a4900 100644 --- a/js/codeq/login.js +++ b/js/codeq/login.js @@ -19,6 +19,7 @@ .fail(function (reason) { $('#disabled').css('display', 'none'); $('#disabled').css('cursor', ''); + codeq.log.error('Login failed: ' + reason, reason); alert('Login request failed: ' + reason); }) .done(); -- cgit v1.2.1 From c922a97f9810321eb6812cf52c463454617b82d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Fri, 18 Sep 2015 16:34:54 +0200 Subject: Don't crash loading a python problem, if the problem has no plan set. --- js/codeq/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js') diff --git a/js/codeq/python.js b/js/codeq/python.js index 1d4ee17..a8d4c95 100644 --- a/js/codeq/python.js +++ b/js/codeq/python.js @@ -321,7 +321,7 @@ editor.setValue(info.solution); $('#title').text(problem.slug); jqDescription.html(problem.description); - $('#btn_code_plan').prop('disabled', problem.plan.length == 0); + $('#btn_code_plan').prop('disabled', (problem.plan || '').length == 0); editor.on('change', function (instance, changeObj) { var doc = editor.getDoc(), -- cgit v1.2.1 From 3df482a0fcca2f11cce51fc072e6a4dbc861c2a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Fri, 18 Sep 2015 16:36:21 +0200 Subject: Implement console history and enable it with python problems. --- js/codeq/console.js | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++--- js/codeq/python.js | 3 ++- 2 files changed, 68 insertions(+), 5 deletions(-) (limited to 'js') diff --git a/js/codeq/console.js b/js/codeq/console.js index 57d5a07..55e9909 100644 --- a/js/codeq/console.js +++ b/js/codeq/console.js @@ -10,8 +10,8 @@ '000': { 'ArrowLeft': function () { this.moveLeft(); return false; }, 'ArrowRight': function () { this.moveRight(); return false; }, - 'ArrowUp': noop, - 'ArrowDown': noop, + 'ArrowUp': function () { this.showPreviousHistory(); return false; }, + 'ArrowDown': function () { this.showNextHistory(); return false; }, 'Backspace': function () { this.deleteCharacterLeft(); return false; }, 'Delete': function () { this.deleteCharacterRight(); return false; }, 'End': function () { this.moveToEndOfLine(); return false; }, @@ -168,6 +168,10 @@ jqInput = $(''), // for receiving keypresses, clipboard content inputDisabled = false, // whether to ignore input lineBuffered = true, // whether we are line-buffered or not buffered + autoHistory = (options && options.autoHistory) || false, // whether to auto-decide what goes into the history buffer + history = [], // the list of entered lines, it is only appended + currentHistory = [], // the list of entered lines, some possibly modified (it is copied from history on each new line editing) + currentHistoryIndex = -1, renderSpan = function (lineDescriptor, startCol, length) { var jqLine = lineDescriptor.jqLine, @@ -431,6 +435,20 @@ jqElt.scrollTop(dh); }, + stashCurrentHistory = function () { + var leftmost = typeof handler.leftmostCol === 'number' && handler.leftmostCol || 0, + lineDescriptor = lines[currentRow], + line = lineDescriptor.content.substring(leftmost); + if (currentHistoryIndex < 0) { + currentHistoryIndex = currentHistory.length; + currentHistory.push(line); + } + else { + currentHistory[currentHistoryIndex] = line; + } + return lineDescriptor; + }, + // the handler object that is returned handler = { 'setLineBuffered': function () { lineBuffered = true; }, @@ -735,6 +753,46 @@ jqElt.empty(); jqElt = null; options = null; + }, + + 'addHistory': function (line) { + history.push(line); + }, + + 'currentInput': function () { + var leftmost = typeof handler.leftmostCol === 'number' && handler.leftmostCol || 0; + return lines[currentRow].content.substring(leftmost); + }, + + 'resetCurrentHistory': function () { + var i; + currentHistory.length = 0; + for (i = 0; i < history.length; i++) currentHistory.push(history[i]); + currentHistoryIndex = -1; + }, + + 'showPreviousHistory': function () { + var leftmost = typeof handler.leftmostCol === 'number' && handler.leftmostCol || 0, + lineDescriptor = stashCurrentHistory(); + if (currentHistoryIndex > 0) { + currentHistoryIndex--; + lineDescriptor.content = lineDescriptor.content.substring(0, leftmost) + currentHistory[currentHistoryIndex]; + currentCol = lineDescriptor.content.length; + lineDescriptor.classNames = mergeClasses(classesBetween(lineDescriptor.classNames, 0, leftmost), makeClassDescriptor('input', leftmost, currentCol - leftmost)); + renderLine(lineDescriptor); + } + }, + + 'showNextHistory': function () { + var leftmost = typeof handler.leftmostCol === 'number' && handler.leftmostCol || 0, + lineDescriptor = stashCurrentHistory(); + if (currentHistoryIndex < (currentHistory.length - 1)) { + currentHistoryIndex++; + lineDescriptor.content = lineDescriptor.content.substring(0, leftmost) + currentHistory[currentHistoryIndex]; + currentCol = lineDescriptor.content.length; + lineDescriptor.classNames = mergeClasses(classesBetween(lineDescriptor.classNames, 0, leftmost), makeClassDescriptor('input', leftmost, currentCol - leftmost)); + renderLine(lineDescriptor); + } } }, @@ -775,7 +833,8 @@ .then(scrollCursorIntoView) // scroll to bottom on input .then((function (cookedLine, cookedLineIndex) {return function () { var thisRow = currentRow, - leftmost = typeof handler.leftmostCol === 'number' && handler.leftmostCol || 0; + leftmost = typeof handler.leftmostCol === 'number' && handler.leftmostCol || 0, + line; if (cookedLine) handler.insertAtCursor(cookedLine, 'input'); // append what we have to the display if (cookedLineIndex < lastCookedLineIndex) { @@ -791,7 +850,10 @@ if (thisRow < currentRow) { // in line-buffered mode emit each line separately, except for the last (current) line try { - return handler.onInput(lines[thisRow].content.substring(leftmost)); + line = lines[thisRow].content.substring(leftmost); + if (autoHistory) handler.addHistory(line); // auto-history works only in line-buffered mode + handler.resetCurrentHistory(); + return handler.onInput(line); } catch (e) { codeq.log.error('Error while invoking terminal onInput: ' + e, e); diff --git a/js/codeq/python.js b/js/codeq/python.js index a8d4c95..c5f7df6 100644 --- a/js/codeq/python.js +++ b/js/codeq/python.js @@ -225,7 +225,8 @@ var makePythonTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) { var terminal = codeq.makeConsole(jqConsole, { - 'greeting': 'CodeQ Python terminal proxy' + 'greeting': 'CodeQ Python terminal proxy', + 'autoHistory': true }), tcs = function terminalCommandSuccess (data) { if (data.code !== 0) { -- cgit v1.2.1 From da92df9761b52f6d24d2241080121cc5bef5e932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Fri, 18 Sep 2015 16:41:12 +0200 Subject: Don't crash loading a prolog problem, if the problem has no plan set. --- js/codeq/prolog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js') diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js index 4eed354..be4791a 100644 --- a/js/codeq/prolog.js +++ b/js/codeq/prolog.js @@ -371,7 +371,7 @@ editor.setValue(info.solution); $('#title').text(problem.slug); jqDescription.html(problem.description); - $('#btn_code_plan').prop('disabled', problem.plan.length == 0); + $('#btn_code_plan').prop('disabled', (problem.plan || '').length == 0); editor.on('change', function (instance, changeObj) { var doc = editor.getDoc(), -- cgit v1.2.1 From 45e1dc64a3f36222eb775a00157f587ebdaabd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Fri, 18 Sep 2015 16:41:40 +0200 Subject: Enable auto-history in prolog console. --- js/codeq/prolog.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'js') diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js index be4791a..e411bc4 100644 --- a/js/codeq/prolog.js +++ b/js/codeq/prolog.js @@ -227,7 +227,8 @@ var promptMode = true, // default: query composition; alternative: query result browsing manualStop = false,// if the user stopped showing next answers (false) or if there are no more answers (true) terminal = codeq.makeConsole(jqConsole, { - 'greeting': 'CodeQ Prolog terminal proxy' + 'greeting': 'CodeQ Prolog terminal proxy', + 'autoHistory': true }), tcs = function terminalCommandSuccess (data) { var t, lines, i; -- cgit v1.2.1 From 5ae5ffc6e86a0181799b6f45167bfb57ea91a32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Fri, 18 Sep 2015 18:39:55 +0200 Subject: Use a link instead of a button in a static hint sequence. --- js/codeq/hint.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'js') diff --git a/js/codeq/hint.js b/js/codeq/hint.js index c06aef7..55acac9 100644 --- a/js/codeq/hint.js +++ b/js/codeq/hint.js @@ -46,7 +46,7 @@ typeHandlers = { 'static': function (type, template, serverHint) { var args = serverHint.args, - jqContainer, jqButton, i, N; + jqContainer, jqButton, i, N, tmpl, tmplIsObject; if (template instanceof Array) { // unwrap the template if there's only one if (template.length == 0) template = ''; else if (template.length == 1) template = template[0] + ''; // it must be a string @@ -54,17 +54,23 @@ if (template instanceof Array) { codeq.log.debug('Processing an array of static hints'); jqContainer = $('
'); - jqButton = $(''); // TODO: translate "more" + jqButton = $(''); jqHints.append(jqContainer); N = template.length; - jqContainer.append('
' + processTemplate(template[0], args) + '
'); + tmpl = template[0]; + tmplIsObject = (typeof tmpl === 'object') && (tmpl !== null); + jqContainer.append('
' + processTemplate((tmplIsObject ? tmpl.message : tmpl) || '', args) + '
'); jqContainer.append(jqButton); + jqButton.text(tmplIsObject && tmpl.linkText ? tmpl.linkText : 'More...'); // TODO: translate "more" i = 1; jqButton.on('click', function () { - var jqNext = $('
' + processTemplate(template[i], args) + '
'); + var tmpl = template[i], + tmplIsObject = (typeof tmpl === 'object') && (tmpl !== null), + jqNext = $('
' + processTemplate((tmplIsObject ? tmpl.message : tmpl) || '', args) + '
'); i++; if (i < N) { jqButton.before(jqNext); + jqButton.text(tmplIsObject && tmpl.linkText ? tmpl.linkText : 'More...'); // TODO: translate "more" } else { jqButton.remove(); -- cgit v1.2.1