<commit_before>/*
 * Copyright 2019 The CodeWorld Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const autohelpEnabled = location.hash.length <= 2;

/*
 * Initializes the programming environment.  This is called after the
 * entire document body and other JavaScript has loaded.
 */
async function init() {
    await Alert.init();
    await Auth.init();

    // Keep the base bundle preloaded by retrying regularly.
    function preloadBaseBundle() {
        const request = new XMLHttpRequest();
        request.open('GET', '/runBaseJS', true);
        request.setRequestHeader('Cache-control', 'max-stale');
        request.send();
    }
    preloadBaseBundle();
    window.setInterval(preloadBaseBundle, 1000 * 60 * 60);

    window.allProjectNames = [
        []
    ];
    window.allFolderNames = [
        []
    ];
    window.openProjectName = null;
    window.nestedDirs = [''];

    window.savedGeneration = null;
    window.runningGeneration = null;

    if (window.location.pathname === '/haskell') {
        window.buildMode = 'haskell';
    } else {
        window.buildMode = 'codeworld';
    }
    document.documentElement.classList.add(window.buildMode);

    window.cancelCompile = () => {};

    let hash = location.hash.slice(1);
    if (hash.length > 0) {
        if (hash.slice(-2) === '==') {
            hash = hash.slice(0, -2);
        }
        if (hash[0] === 'F') {
            sweetAlert({
                title: Alert.title('Save As', 'mdi-cloud-upload'),
                html: 'Enter a name for the shared folder:',
                input: 'text',
                confirmButtonText: 'Save',
                showCancelButton: false,
                closeOnConfirm: false
            }).then(result => {
                if (!result) {
                    return;
                }

                const data = new FormData();
                data.append('mode', window.buildMode);
                data.append('shash', hash);
                data.append('name', result.value);

                sendHttp('POST', 'shareContent', data, request => {
                    window.location.hash = '';
                    if (request.status === 200) {
                        sweetAlert('Success!',
                            'The shared folder has been copied to your root directory.',
                            'success');
                    } else {
                        sweetAlert('Oops!',
                            'Could not load the shared directory. Please try again.',
                            'error');
                    }
                    initCodeworld();
                    registerStandardHints(() => {
                        setMode(true);
                        parseSymbolsFromCurrentCode();
                    });
                    discoverProjects('', 0);
                    updateUI();
                });
            });
        } else {
            initCodeworld();
            registerStandardHints(() => {
                setMode(true);
                parseSymbolsFromCurrentCode();
            });
            updateUI();
        }
    } else {
        initCodeworld();
        registerStandardHints(() => {
            setMode(true);
            parseSymbolsFromCurrentCode();
        });
        updateUI();
    }

    if (hash.length > 0) {
        if (hash.slice(-2) === '==') {
            hash = hash.slice(0, -2);
        }
        if (hash[0] === 'P') {
            sendHttp('GET', `loadSource?hash=${hash}&mode=${window.buildMode}`,
                null, request => {
                    if (request.status === 200) {
                        setCode(request.responseText, null, null, true);
                    }
                });
        } else if (hash[0] !== 'F') {
            setCode('');
        }
    } else {
        setCode('');
    }
}

function initCodeworld() {
    const editor = document.getElementById('editor');

    window.codeworldKeywords = {};

    window.codeworldEditor = CodeMirror.fromTextArea(editor, {
        mode: {
            name: 'codeworld',
            overrideKeywords: window.codeworldKeywords
        },
        undoDepth: 50,
        lineNumbers: true,
        autofocus: true,
        matchBrackets: true,
        styleActiveLine: !WURFL || !WURFL.is_mobile,
        showTrailingSpace: true,
        indentWithTabs: false,
        autoClearEmptyLines: true,
        highlightSelectionMatches: {
            showToken: /\w/,
            annotateScrollbar: true
        },
        rulers: [{
            column: 80,
            color: '#bbb',
            lineStyle: 'dashed'
        }],
        extraKeys: {
            'Ctrl-Space': 'autocomplete',
            'Shift-Space': 'autocomplete',
            'Tab': 'indentMore',
            'Shift-Tab': 'indentLess',
            'Ctrl-Enter': compile,
            'Ctrl-Up': changeFontSize(1),
            'Ctrl-Down': changeFontSize(-1)
        },
        textHover: onHover,
        gutters: ['CodeMirror-lint-markers'],
        lint: {
            getAnnotations: (text, callback) => {
                if (text.trim() === '') {
                    callback([]);
                    return;
                }

                let request = null;

                function cancelLintRequest() {
                    if (window.codeworldEditor) {
                        window.codeworldEditor.off('change',
                            cancelLintRequest);
                    }
                    if (request) request.abort();
                }

                const data = new FormData();
                data.append('source', text);
                data.append('mode', window.buildMode);
                request = sendHttp('POST', 'errorCheck', data,
                    request => {
                        if (window.codeworldEditor) {
                            window.codeworldEditor.off('change',
                                cancelLintRequest);
                        }

                        if (request.status === 400 || request.status === 200) {
                            const messages = parseCompileErrors(request.responseText);
                            callback(messages);
                        } else if (request.status === 0) {
                            // Request was cancelled because of a later change.  Do nothing.
                        } else {
                            console.log(
                                'Not expected behavior: don\'t know how to ' +
                                'handle request with status ',
                                request.status, request);
                        }
                    });
                if (window.codeworldEditor) {
                    window.codeworldEditor.on('change',
                        cancelLintRequest);
                }
            },
            async: true
        }
    });
    window.codeworldEditor.refresh();
    window.codeworldEditor.on("cursorActivity", function() {
        const prevDiv = document.getElementById("function-details");
        if (prevDiv) prevDiv.remove();

        const cursor = window.codeworldEditor.getCursor();
        const currentToken = window.codeworldEditor.getTokenAt(cursor);
        const functions = currentToken.state.contexts.filter(ctx => ctx.functionName);

        if (!functions.length) return;

        const { functionName, argIndex } = functions.pop();
        const keywordData = window.codeWorldSymbols[functionName];

        // don't show tooltip if function details or argument types are not known
        if (!keywordData || keywordData.declaration === functionName) return;

        const topDiv = document.createElement('div');

        topDiv.title = functionName;
        topDiv.id = "function-details";

        const docDiv = document.createElement('div');
        docDiv.classList.add("function-tooltip-styling");

        const annotation = document.createElement('div');
        renderDeclaration(annotation, functionName, keywordData, 9999, argIndex);
        annotation.className = 'hover-decl';
        docDiv.appendChild(annotation);

        topDiv.appendChild(docDiv);
        window.codeworldEditor.addWidget(cursor, topDiv, true, "above", "left");
    });<commit_msg>Don't try to show function-arg tooltips for haskell mode.<commit_after>/*
 * Copyright 2019 The CodeWorld Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const autohelpEnabled = location.hash.length <= 2;

/*
 * Initializes the programming environment.  This is called after the
 * entire document body and other JavaScript has loaded.
 */
async function init() {
    await Alert.init();
    await Auth.init();

    // Keep the base bundle preloaded by retrying regularly.
    function preloadBaseBundle() {
        const request = new XMLHttpRequest();
        request.open('GET', '/runBaseJS', true);
        request.setRequestHeader('Cache-control', 'max-stale');
        request.send();
    }
    preloadBaseBundle();
    window.setInterval(preloadBaseBundle, 1000 * 60 * 60);

    window.allProjectNames = [
        []
    ];
    window.allFolderNames = [
        []
    ];
    window.openProjectName = null;
    window.nestedDirs = [''];

    window.savedGeneration = null;
    window.runningGeneration = null;

    if (window.location.pathname === '/haskell') {
        window.buildMode = 'haskell';
    } else {
        window.buildMode = 'codeworld';
    }
    document.documentElement.classList.add(window.buildMode);

    window.cancelCompile = () => {};

    let hash = location.hash.slice(1);
    if (hash.length > 0) {
        if (hash.slice(-2) === '==') {
            hash = hash.slice(0, -2);
        }
        if (hash[0] === 'F') {
            sweetAlert({
                title: Alert.title('Save As', 'mdi-cloud-upload'),
                html: 'Enter a name for the shared folder:',
                input: 'text',
                confirmButtonText: 'Save',
                showCancelButton: false,
                closeOnConfirm: false
            }).then(result => {
                if (!result) {
                    return;
                }

                const data = new FormData();
                data.append('mode', window.buildMode);
                data.append('shash', hash);
                data.append('name', result.value);

                sendHttp('POST', 'shareContent', data, request => {
                    window.location.hash = '';
                    if (request.status === 200) {
                        sweetAlert('Success!',
                            'The shared folder has been copied to your root directory.',
                            'success');
                    } else {
                        sweetAlert('Oops!',
                            'Could not load the shared directory. Please try again.',
                            'error');
                    }
                    initCodeworld();
                    registerStandardHints(() => {
                        setMode(true);
                        parseSymbolsFromCurrentCode();
                    });
                    discoverProjects('', 0);
                    updateUI();
                });
            });
        } else {
            initCodeworld();
            registerStandardHints(() => {
                setMode(true);
                parseSymbolsFromCurrentCode();
            });
            updateUI();
        }
    } else {
        initCodeworld();
        registerStandardHints(() => {
            setMode(true);
            parseSymbolsFromCurrentCode();
        });
        updateUI();
    }

    if (hash.length > 0) {
        if (hash.slice(-2) === '==') {
            hash = hash.slice(0, -2);
        }
        if (hash[0] === 'P') {
            sendHttp('GET', `loadSource?hash=${hash}&mode=${window.buildMode}`,
                null, request => {
                    if (request.status === 200) {
                        setCode(request.responseText, null, null, true);
                    }
                });
        } else if (hash[0] !== 'F') {
            setCode('');
        }
    } else {
        setCode('');
    }
}

function initCodeworld() {
    const editor = document.getElementById('editor');

    window.codeworldKeywords = {};

    window.codeworldEditor = CodeMirror.fromTextArea(editor, {
        mode: {
            name: 'codeworld',
            overrideKeywords: window.codeworldKeywords
        },
        undoDepth: 50,
        lineNumbers: true,
        autofocus: true,
        matchBrackets: true,
        styleActiveLine: !WURFL || !WURFL.is_mobile,
        showTrailingSpace: true,
        indentWithTabs: false,
        autoClearEmptyLines: true,
        highlightSelectionMatches: {
            showToken: /\w/,
            annotateScrollbar: true
        },
        rulers: [{
            column: 80,
            color: '#bbb',
            lineStyle: 'dashed'
        }],
        extraKeys: {
            'Ctrl-Space': 'autocomplete',
            'Shift-Space': 'autocomplete',
            'Tab': 'indentMore',
            'Shift-Tab': 'indentLess',
            'Ctrl-Enter': compile,
            'Ctrl-Up': changeFontSize(1),
            'Ctrl-Down': changeFontSize(-1)
        },
        textHover: onHover,
        gutters: ['CodeMirror-lint-markers'],
        lint: {
            getAnnotations: (text, callback) => {
                if (text.trim() === '') {
                    callback([]);
                    return;
                }

                let request = null;

                function cancelLintRequest() {
                    if (window.codeworldEditor) {
                        window.codeworldEditor.off('change',
                            cancelLintRequest);
                    }
                    if (request) request.abort();
                }

                const data = new FormData();
                data.append('source', text);
                data.append('mode', window.buildMode);
                request = sendHttp('POST', 'errorCheck', data,
                    request => {
                        if (window.codeworldEditor) {
                            window.codeworldEditor.off('change',
                                cancelLintRequest);
                        }

                        if (request.status === 400 || request.status === 200) {
                            const messages = parseCompileErrors(request.responseText);
                            callback(messages);
                        } else if (request.status === 0) {
                            // Request was cancelled because of a later change.  Do nothing.
                        } else {
                            console.log(
                                'Not expected behavior: don\'t know how to ' +
                                'handle request with status ',
                                request.status, request);
                        }
                    });
                if (window.codeworldEditor) {
                    window.codeworldEditor.on('change',
                        cancelLintRequest);
                }
            },
            async: true
        }
    });
    window.codeworldEditor.refresh();
    window.codeworldEditor.on("cursorActivity", function() {
        if (window.buildMode !== 'codeworld') {
            return;
        }

        const prevDiv = document.getElementById("function-details");
        if (prevDiv) prevDiv.remove();

        const cursor = window.codeworldEditor.getCursor();
        const currentToken = window.codeworldEditor.getTokenAt(cursor);
        const functions = currentToken.state.contexts.filter(ctx => ctx.functionName);

        if (!functions.length) return;

        const { functionName, argIndex } = functions.pop();
        const keywordData = window.codeWorldSymbols[functionName];

        // don't show tooltip if function details or argument types are not known
        if (!keywordData || keywordData.declaration === functionName) return;

        const topDiv = document.createElement('div');

        topDiv.title = functionName;
        topDiv.id = "function-details";

        const docDiv = document.createElement('div');
        docDiv.classList.add("function-tooltip-styling");

        const annotation = document.createElement('div');
        renderDeclaration(annotation, functionName, keywordData, 9999, argIndex);
        annotation.className = 'hover-decl';
        docDiv.appendChild(annotation);

        topDiv.appendChild(docDiv);
        window.codeworldEditor.addWidget(cursor, topDiv, true, "above", "left");
    });
{'fraction_non_alphanumeric': 0.11462160176340926, 'fraction_numerical': 0.0059910699146555136, 'mean_word_length': 1.5133522727272728, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5406773', 'n_tokens_mistral': 4513, 'n_tokens_neox': 4232, 'n_words': 1024}