summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--css/codeq/hint.css3
-rw-r--r--index.html2
-rw-r--r--js/codeq/console.js70
-rw-r--r--js/codeq/hint.js27
-rw-r--r--js/codeq/login.js1
-rw-r--r--js/codeq/prolog.js18
-rw-r--r--js/codeq/python.js16
7 files changed, 123 insertions, 14 deletions
diff --git a/css/codeq/hint.css b/css/codeq/hint.css
new file mode 100644
index 0000000..76090b2
--- /dev/null
+++ b/css/codeq/hint.css
@@ -0,0 +1,3 @@
+a.hint-static-link {
+ cursor: pointer;
+} \ No newline at end of file
diff --git a/index.html b/index.html
index 8f4e933..a767ff4 100644
--- a/index.html
+++ b/index.html
@@ -15,6 +15,7 @@
<!-- App -->
<link rel="stylesheet" href="css/codeq.css" type="text/css">
<link rel="stylesheet" href="css/codeq/console.css" type="text/css">
+ <link rel="stylesheet" href="css/codeq/hint.css" type="text/css">
<title>CodeQ</title>
</head>
<body>
@@ -126,6 +127,7 @@
<div class="col-lg-3 col-md-6 col-sm-12 block" id="code_editor_outer_div">
<nav class="navbar navbar-default" id="block-toolbar">
<div class="container-fluid">
+ <button type="button" class="btn btn-default navbar-btn" id="btn_code_plan">Plan</button>
<button type="button" class="btn btn-default navbar-btn" id="btn_code_hint">Hint</button>
<button type="button" class="btn btn-default navbar-btn" id="btn_code_test">Test</button>
</div>
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 = $('<textarea style="left: -9999px; bottom: 0; width: 2px; position: absolute;"></textarea>'), // 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/hint.js b/js/codeq/hint.js
index 06eceb0..55acac9 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;
@@ -45,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
@@ -53,17 +54,23 @@
if (template instanceof Array) {
codeq.log.debug('Processing an array of static hints');
jqContainer = $('<div class="hint-static-group"></div>');
- jqButton = $('<button type="button">More...</button>'); // TODO: translate "more"
+ jqButton = $('<a class="hint-static-link"></a>');
jqHints.append(jqContainer);
N = template.length;
- jqContainer.append('<div class="hint-static">' + processTemplate(template[0], args) + '</div>');
+ tmpl = template[0];
+ tmplIsObject = (typeof tmpl === 'object') && (tmpl !== null);
+ jqContainer.append('<div class="hint-static">' + processTemplate((tmplIsObject ? tmpl.message : tmpl) || '', args) + '</div>');
jqContainer.append(jqButton);
+ jqButton.text(tmplIsObject && tmpl.linkText ? tmpl.linkText : 'More...'); // TODO: translate "more"
i = 1;
jqButton.on('click', function () {
- var jqNext = $('<div class="hint-static">' + processTemplate(template[i], args) + '</div>');
+ var tmpl = template[i],
+ tmplIsObject = (typeof tmpl === 'object') && (tmpl !== null),
+ jqNext = $('<div class="hint-static">' + processTemplate((tmplIsObject ? tmpl.message : tmpl) || '', args) + '</div>');
i++;
if (i < N) {
jqButton.before(jqNext);
+ jqButton.text(tmplIsObject && tmpl.linkText ? tmpl.linkText : 'More...'); // TODO: translate "more"
}
else {
jqButton.remove();
@@ -124,6 +131,16 @@
};
return {
+ /** Display the next "planning" hint and return whether there are
+ * any more available.
+ */
+ 'planNext': function () {
+ if (planIdx < planDef.length) {
+ jqHints.append('<div class="plan">' + planDef[planIdx++] + '</div>');
+ }
+ 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/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();
diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js
index 96c3077..e411bc4 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);
},
@@ -223,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;
@@ -362,11 +367,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 +387,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 +463,4 @@
}
};
};
-})(); \ No newline at end of file
+})();
diff --git a/js/codeq/python.js b/js/codeq/python.js
index e856c08..c5f7df6 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);
},
@@ -221,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) {
@@ -312,11 +317,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 +337,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({