diff --git a/backend/static/js/codemirror/modes/sparql/sparql.js b/backend/static/js/codemirror/modes/sparql/sparql.js index 778558fc..955a1bda 100644 --- a/backend/static/js/codemirror/modes/sparql/sparql.js +++ b/backend/static/js/codemirror/modes/sparql/sparql.js @@ -74,14 +74,14 @@ const CONTEXTS = [ const COMPLEXTYPES = [ { name: 'PREFIX', - definition: /PREFIX (.*)/g, + definition: /PREFIX (.*)/gi, suggestions: [['PREFIX ', function (c) { return getPrefixSuggestions(c); }, '\n']], availableInContext: ['PrefixDecl', 'undefined'], }, { name: 'SELECT', - definition: /SELECT (.*)/g, + definition: /SELECT (.*)/gi, suggestions: [['SELECT * WHERE {\n \n}\n']], availableInContext: ['PrefixDecl', 'undefined', 'SubQuery'], onlyOnce: true, @@ -89,7 +89,7 @@ const COMPLEXTYPES = [ }, { name: 'DISTINCT', - definition: /DISTINCT|[?\w]+ [?\w]*/g, + definition: /DISTINCT|[?\w]+ [?\w]*/gi, suggestions: [['DISTINCT ']], availableInContext: ['SelectClause'], onlyOnce: true, @@ -113,70 +113,70 @@ const COMPLEXTYPES = [ }, { name: 'TEXT', - definition: /TEXT\((.*)\)/g, + definition: /TEXT\((.*)\)/gi, suggestions: [['TEXT(', function (c) { var a = []; $(getVariables(c, true, "text")).each(function (k, v) { a.push(v) }); return a; }, ') ']], availableInContext: ['SelectClause'], }, { name: 'SCORE', - definition: /SCORE\((.*)\)/g, + definition: /SCORE\((.*)\)/gi, suggestions: [['SCORE(', function (c) { var a = []; $(getVariables(c, true, "text")).each(function (k, v) { a.push(v) }); return a; }, ') ']], availableInContext: ['SelectClause', 'OrderCondition'], }, { name: 'MIN', - definition: /\(MIN\((.*)\) as (.*)\)/g, + definition: /\(MIN\((.*)\) as (.*)\)/gi, suggestions: [['(MIN(', function (c) { if (getContextByName('SolutionModifier') && getContextByName('SolutionModifier')['content'].indexOf('GROUP BY') != -1) { return getVariables(c, true); } else { return false; } }, ') as ?min_{[0]}) ']], availableInContext: ['SelectClause', 'OrderCondition'], }, { name: 'MAX', - definition: /\(MAX\((.*)\) as (.*)\)/g, + definition: /\(MAX\((.*)\) as (.*)\)/gi, suggestions: [['(MAX(', function (c) { if (getContextByName('SolutionModifier') && getContextByName('SolutionModifier')['content'].indexOf('GROUP BY') != -1) { return getVariables(c, true); } else { return false; } }, ') as ?max_{[0]}) ']], availableInContext: ['SelectClause', 'OrderCondition'], }, { name: 'SUM', - definition: /\(SUM\((.*)\) as (.*)\)/g, + definition: /\(SUM\((.*)\) as (.*)\)/gi, suggestions: [['(SUM(', ['DISTINCT ', ''], function (c) { if (getContextByName('SolutionModifier') && getContextByName('SolutionModifier')['content'].indexOf('GROUP BY') != -1) { return getVariables(c, true); } else { return false; } }, ') as ?sum_{[1]}) ']], availableInContext: ['SelectClause', 'OrderCondition'], }, { name: 'AVG', - definition: /\(AVG\((.*)\) as (.*)\)/g, + definition: /\(AVG\((.*)\) as (.*)\)/gi, suggestions: [['(AVG(', ['DISTINCT ', ''], function (c) { if (getContextByName('SolutionModifier') && getContextByName('SolutionModifier')['content'].indexOf('GROUP BY') != -1) { return getVariables(c, true); } else { return false; } }, ') as ?avg_{[1]}) ']], availableInContext: ['SelectClause', 'OrderCondition'], }, { name: 'SAMPLE', - definition: /\(SAMPLE\((.*)\) as (.*)\)/g, + definition: /\(SAMPLE\((.*)\) as (.*)\)/gi, suggestions: [['(SAMPLE(', function (c) { if (getContextByName('SolutionModifier') && getContextByName('SolutionModifier')['content'].indexOf('GROUP BY') != -1) { return getVariables(c, true); } else { return false; } }, ') as ?sample_{[0]}) ']], availableInContext: ['SelectClause'], }, { name: 'COUNT', - definition: /\(COUNT\((.*)\) as (.*)\)/g, + definition: /\(COUNT\((.*)\) as (.*)\)/gi, suggestions: [['(COUNT(', ['DISTINCT ', ''], function (c) { if (getContextByName('SolutionModifier') && getContextByName('SolutionModifier')['content'].indexOf('GROUP BY') != -1) { return getVariables(c, true); } else { return false; } }, ') as ?count_{[1]}) ']], availableInContext: ['SelectClause'], }, { name: 'GROUP_CONCAT', - definition: /\(GROUP_CONCAT\((.*)\) as (.*)\)/g, + definition: /\(GROUP_CONCAT\((.*)\) as (.*)\)/gi, suggestions: [['(GROUP_CONCAT(', ['DISTINCT ', ''], function (c) { if (getContextByName('SolutionModifier') && getContextByName('SolutionModifier')['content'].indexOf('GROUP BY') != -1) { return getVariables(c, true); } else { return false; } }, ') as ?concat_{[1]}) ']], availableInContext: ['SelectClause'], }, { name: 'LIMIT', - definition: /\bLIMIT ([0-9+])/g, + definition: /\bLIMIT ([0-9+])/gi, suggestions: [['LIMIT ', [1, 10, 100, 1000], '\n']], availableInContext: ['SolutionModifier'], onlyOnce: true, @@ -184,7 +184,7 @@ const COMPLEXTYPES = [ }, { name: 'TEXTLIMIT', - definition: /TEXTLIMIT ([0-9+])/g, + definition: /TEXTLIMIT ([0-9+])/gi, suggestions: [['TEXTLIMIT ', [2, 5, 10], '\n']], availableInContext: ['SolutionModifier'], onlyOnce: true, @@ -192,21 +192,21 @@ const COMPLEXTYPES = [ }, { name: 'ASC', - definition: /ASC(\?.*)/g, + definition: /ASC(\?.*)/gi, suggestions: [['ASC(', function (c) { var a = getVariables(c); for (var v of getVariables(c, undefined, "text")) { a.push("SCORE(" + v + ")"); }; return a; }, ') ']], availableInContext: ['OrderCondition'], }, { name: 'DESC', - definition: /DESC(\?.*)/g, + definition: /DESC(\?.*)/gi, suggestions: [['DESC(', function (c) { var a = getVariables(c); for (var v of getVariables(c, undefined, "text")) { a.push("SCORE(" + v + ")"); }; return a; }, ') ']], availableInContext: ['OrderCondition'], }, { name: 'ORDER BY', - definition: /ORDER BY .*/g, + definition: /ORDER BY .*/gi, suggestions: [['ORDER BY ']], availableInContext: ['SolutionModifier'], onlyOnce: true, @@ -214,7 +214,7 @@ const COMPLEXTYPES = [ }, { name: 'GROUP BY', - definition: /GROUP BY \?(.+)/g, + definition: /GROUP BY \?(.+)/gi, suggestions: [['GROUP BY ']], availableInContext: ['SolutionModifier'], onlyOnce: true, @@ -222,7 +222,7 @@ const COMPLEXTYPES = [ }, { name: 'HAVING', - definition: /HAVING\?(.+)/g, + definition: /HAVING\?(.+)/gi, suggestions: [['HAVING(', function (c) { return getVariables(c); }, ' ']], availableInContext: ['SolutionModifier'], onlyOnce: true, @@ -236,7 +236,7 @@ const COMPLEXTYPES = [ }, { name: 'REGEX', - definition: /REGEX(\?.*)/g, + definition: /REGEX(\?.*)/gi, suggestions: [['REGEX(', function (c) { return getVariables(c, true); }]], onlyOncePerVariation: true, availableInContext: ['Filter'], @@ -244,7 +244,7 @@ const COMPLEXTYPES = [ }, { name: 'FILTER', - definition: /FILTER\((.*)/g, + definition: /FILTER\((.*)/gi, suggestions: [['FILTER ']], availableInContext: ['WhereClause', 'OptionalClause', 'UnionClause'], requiresEmptyLine: true, @@ -253,7 +253,7 @@ const COMPLEXTYPES = [ }, { name: 'FILTER LANGUAGE', - definition: /LANG(.*)/g, + definition: /LANG(.*)/gi, suggestions: [['(LANG(', function (c) { var a = []; for (var v of getVariables(c, undefined, undefined, LANGUAGELITERAL)) { if (editor.getValue().indexOf("FILTER(LANG(" + v) == -1) { a.push(v); } }; return a; }, ') = ', LANGUAGES, ') .\n']], availableInContext: ['Filter'], }, diff --git a/backend/static/js/helper.js b/backend/static/js/helper.js index eef748b8..8c2bf9d3 100644 --- a/backend/static/js/helper.js +++ b/backend/static/js/helper.js @@ -219,7 +219,7 @@ function splitSparqlQueryIntoParts(query) { .replace(/\(\s+/g, "(").replace(/\s+\)/g, ")") .replace(/\{\s*/g, "{ ").replace(/\s*\.?\s*\}$/g, " }"); // console.log("SPLIT_SPARQL_QUERY_INTO_PARTS:", query_with_spaces_normalized) - const pattern = /^\s*(.*?)\s*SELECT\s+([^{]*\S)\s*WHERE\s*{\s*(\S.*\S)\s*}\s*(.*?)\s*$/m; + const pattern = /^\s*(.*?)\s*SELECT\s+([^{]*\S)\s*WHERE\s*{\s*(\S.*\S)\s*}\s*(.*?)\s*$/mi; var match = query_normalized.match(pattern); if (!match) { throw "ERROR: Query did not match regex for SELECT queries"; @@ -625,9 +625,9 @@ function cleanLines(cm) { cm.setSelection(selection.anchor, selection.head); } -// Triggered when using TAB +// Triggered when the `TAB` key is pressed; see `qleverUI`, search for +// `extraKeys` in `CodeMirror` initialization. function switchStates(cm) { - var cur = editor.getCursor(); // current cursor position var absolutePosition = editor.indexFromPos({ 'line': cur.line, 'ch': cur.ch + 1 }); // absolute cursor position in text @@ -635,7 +635,7 @@ function switchStates(cm) { var gaps = []; - var gap1 = /WHERE/g + var gap1 = /WHERE/gi while ((match = gap1.exec(content)) != null) { gaps.push(match.index + match[0].length - 5); } @@ -669,12 +669,13 @@ function switchStates(cm) { var newCursor = editor.posFromIndex(found); editor.setCursor(newCursor); + var line = cm.getLine(newCursor.line); indentWhitespaces = (" ".repeat((line.length - line.trimStart().length))) - if (line.slice(newCursor.ch, newCursor.ch + 5) == "WHERE") { - // add empty whitespace in select if not present + if (line.slice(newCursor.ch, newCursor.ch + 5).toUpperCase() == "WHERE") { + // Add empty whitespace in select if not present log("Found SELECT-Placeholder on postion " + found, 'other'); cm.setSelection({ 'line': newCursor.line, 'ch': line.length - 8 }, { 'line': newCursor.line, 'ch': line.length - 7 }); cm.replaceSelection(" "); @@ -702,6 +703,12 @@ function switchStates(cm) { cm.setSelection(cm.getCursor(), cm.getCursor()); + // Now that the cursor is set at a new position, clean up the query. + // + // NOTE: Previously, this was done after every cursor movement in + // `qleverUI.js`, where it says `editor.on("cursorActivity", ...)`. + cleanLines(cm); + window.setTimeout(function () { CodeMirror.commands.autocomplete(editor); }, 100); @@ -918,6 +925,7 @@ function getFormattedResultEntry(str, maxLength, column = undefined) { // the column header. // console.log("Check if \"" + str + "\" in column \"" + var_name + "\" is a float ..."); if (var_name.endsWith("?note") || var_name.endsWith("_note")) str = parseFloat(str).toFixed(2).toString(); + if (var_name.endsWith("?grade") || var_name.endsWith("_grade")) str = parseFloat(str).toFixed(2).toString(); if (var_name.endsWith("_per_paper")) str = parseFloat(str).toFixed(2).toString(); if (var_name.endsWith("_perc") || var_name.endsWith("percent")) str = parseFloat(str).toFixed(2).toString(); if (var_name.endsWith("?lp_proz")) str = parseFloat(str).toFixed(0).toString(); diff --git a/backend/static/js/qleverUI.js b/backend/static/js/qleverUI.js index 58bcdf6d..1d44ac86 100755 --- a/backend/static/js/qleverUI.js +++ b/backend/static/js/qleverUI.js @@ -87,10 +87,10 @@ $(document).ready(function () { // Initialization done. log('Editor initialized', 'other'); - // Do some custom activities on cursor activity + // When cursor moves, make sure that tooltips are closed. editor.on("cursorActivity", function (instance) { $('[data-tooltip=tooltip]').tooltip('hide'); - cleanLines(instance); + // cleanLines(instance); }); editor.on("update", function (instance, event) { @@ -107,10 +107,12 @@ $(document).ready(function () { // Do some custom activities (overwrite codemirror behaviour) editor.on("keyup", function (instance, event) { - // For each prefix in COLLECTEDPREFIXES, check whether it occurs somewhere - // in the query and if so, add it before the first SELECT (and move - // the cursor accordingly). - if (FILLPREFIXES) { + // For each prefix in `COLLECTEDPREFIXES`, check whether it occurs + // somewhere in the query and if so, add it before the first `SELECT` or + // `CONSTRUCT` (and move the cursor accordingly). If there is no `SELECT` + // or `CONSTRUCT`, do nothing. + let select_or_construct_regex = /(^| )(SELECT|CONSTRUCT)/mi; + if (FILLPREFIXES && select_or_construct_regex.test(editor.getValue())) { let queryString = editor.getValue(); let newCursor = editor.getCursor(); let linesAdded = 0; @@ -118,7 +120,7 @@ $(document).ready(function () { const fullPrefix = "PREFIX " + prefix + ": <" + COLLECTEDPREFIXES[prefix] + ">"; if (doesQueryFragmentContainPrefix(queryString, prefix) && queryString.indexOf(fullPrefix) == -1) { - queryString = queryString.replace(/(^| )(SELECT)/m, fullPrefix + "\n$1$2"); + queryString = queryString.replace(select_or_construct_regex, fullPrefix + "\n$1$2"); linesAdded += 1; } } @@ -180,7 +182,7 @@ $(document).ready(function () { for (var line of lines) { if (line.trim().startsWith("PREFIX")) { - var match = /PREFIX (.*): ?<(.*)>/g.exec(line.trim()); + var match = /PREFIX (.*): ?<(.*)>/gi.exec(line.trim()); if (match) { prefixes += line.trim() + '\n'; }