Namespaces
Variants

MediaWiki:Gadget-coliru compiler.js

From cppreference.com

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/*
    Copyright (C) 2013-2026 Povilas Kanapickas and cppreference editors

    This file is part of cppreference.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/.
*/

String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, "");
};

/* We add two new divs for the live editor and the program output. The divs are
   inserted after the geshi code and output respectively. This way the ACE
   editor and the compilation output are independent from the static geshi
   text. We can easily switch between the live editor and the static version.
*/
function Editor(root) {

    var compiler_list_cxx = [
        { title: 'GCC 13.2 (C++98)',  compilerId: 'g132',      userArgs: '-std=c++98' },
        { title: 'GCC 13.2 (C++11)',  compilerId: 'g132',      userArgs: '-std=c++11' },
        { title: 'GCC 13.2 (C++14)',  compilerId: 'g132',      userArgs: '-std=c++14' },
        { title: 'GCC 13.2 (C++17)',  compilerId: 'g132',      userArgs: '-std=c++17' },
        { title: 'GCC 13.2 (C++20)',  compilerId: 'g132',      userArgs: '-std=c++20' },
        { title: 'GCC 13.2 (C++23)',  compilerId: 'g132',      userArgs: '-std=c++23' },
        { title: 'GCC 14.2 (C++23)',  compilerId: 'g142',      userArgs: '-std=c++23' },
        { title: 'GCC 15.2 (C++23)',  compilerId: 'g152',      userArgs: '-std=c++23' },
        { title: 'clang 17 (C++98)',  compilerId: 'clang1701', userArgs: '-std=c++98 -stdlib=libc++' },
        { title: 'clang 17 (C++11)',  compilerId: 'clang1701', userArgs: '-std=c++11 -stdlib=libc++' },
        { title: 'clang 17 (C++14)',  compilerId: 'clang1701', userArgs: '-std=c++14 -stdlib=libc++' },
        { title: 'clang 17 (C++17)',  compilerId: 'clang1701', userArgs: '-std=c++17 -stdlib=libc++' },
        { title: 'clang 17 (C++20)',  compilerId: 'clang1701', userArgs: '-std=c++20 -stdlib=libc++' },
        { title: 'clang 18 (C++23)',  compilerId: 'clang1810',          userArgs: '-std=c++23 -stdlib=libc++' },
        { title: 'clang 20 (C++23)',  compilerId: 'clang2010',          userArgs: '-std=c++23 -stdlib=libc++' },
        { title: 'GCC trunk (C++26)', compilerId: 'gsnapshot',          userArgs: '-std=c++26 -freflection' },
        { title: 'clang trunk (C++26)', compilerId: 'clang_trunk',      userArgs: '-std=c++26 -fmodules-driver -stdlib=libc++' },
        { title: 'MSVC latest (C++)', compilerId: 'vcpp_v19_latest_x64', userArgs: '/std:c++latest /O2 /W4 /EHsc', commonArgs: '' },
    ];
    var cxx_defaults = { gcc: 15, clang: 16, msvc: 17 }; // GCC trunk, clang trunk, MSVC latest
    var cxx_common_args = '-O2 -Wall -Wextra -pedantic -pthread -Wno-unused-command-line-argument  -Wno-unused-variable';

    var compiler_list_c = [
        { title: 'GCC 12.5 (C89)',   compilerId: 'cg125',      userArgs: '-std=c89' },
        { title: 'GCC 12.5 (C99)',   compilerId: 'cg125',      userArgs: '-std=c99' },
        { title: 'GCC 12.5 (C11)',   compilerId: 'cg125',      userArgs: '-std=c11' },
        { title: 'GCC 12.5 (C17)',   compilerId: 'cg125',      userArgs: '-std=c17' },
        { title: 'GCC 15.2 (C23)',   compilerId: 'cg152',      userArgs: '-std=c23' },
        { title: 'clang 15 (C89)',   compilerId: 'cclang1500', userArgs: '-std=c89' },
        { title: 'clang 15 (C99)',   compilerId: 'cclang1500', userArgs: '-std=c99' },
        { title: 'clang 15 (C11)',   compilerId: 'cclang1500', userArgs: '-std=c11' },
        { title: 'clang 15 (C17)',    compilerId: 'cclang1500',          userArgs: '-std=c17' },
        { title: 'GCC trunk (C23)',   compilerId: 'cgsnapshot',          userArgs: '-std=c23' },
        { title: 'clang trunk (C23)', compilerId: 'cclang_trunk',        userArgs: '-std=c23' },
        { title: 'MSVC latest (C)',   compilerId: 'vc_v19_latest_x64',   userArgs: '/std:clatest /O2 /W4 /EHsc', commonArgs: '' },
    ];
    var c_defaults = { gcc: 9, clang: 10, msvc: 11 }; // GCC trunk, clang trunk, MSVC latest
    var c_common_args = '-O2 -Wall -Wextra -pedantic -pthread -lm -Wno-unused-command-line-argument -Wno-unused-variable';

    this.is_cxx = mw.config.get('wgTitle').indexOf('c/') !== 0;

    // read the compiler=... option, if used
    var compiler_hint = $(root).children('.t-example-live-link:first').data('compiler') || 'gcc';

    if (this.is_cxx) {
        this.compiler_list = compiler_list_cxx;
        this.default_id = cxx_defaults[compiler_hint]
        this.common_args = cxx_common_args;
        this.lang = 'c++';
    } else {
        this.compiler_list = compiler_list_c;
        this.default_id = c_defaults[compiler_hint]
        this.common_args = c_common_args;
        this.lang = 'c';
    }

    this.el = {};
    this.el.root = root;
    this.is_orig = true;

    this.el.action_bar = this.el.root.children('.t-example-live-link:first');

    // initialize the action bar
    this.el.run_init_btn = this.el.action_bar.children('.coliru-btn-run-init');
    this.el.run_btn = $('<div>Run</div>')
                        .addClass('coliru-btn coliru-btn-run')
                        .hide().appendTo(this.el.action_bar);
    this.el.share_btn = $('<div>Share</div>')
                        .addClass('coliru-btn coliru-btn-share')
                        .hide().appendTo(this.el.action_bar);
    this.el.exit_btn = $('<div>Exit</div>')
                        .addClass('coliru-btn coliru-btn-exit')
                        .hide().appendTo(this.el.action_bar);

    this.el.cc_select_div = $('<div/>')
                        .addClass('coliru-select-compiler')
                        .hide().appendTo(this.el.action_bar);
    this.el.cc_select = $('<select/>').appendTo(this.el.cc_select_div);

    for (var i = 0; i < this.compiler_list.length; ++i) {
        var cc = this.compiler_list[i];
        $('<option/>').text(cc.title).attr('value', i.toString())
                                     .appendTo(this.el.cc_select);
    }
    this.el.cc_select.val(this.default_id.toString());

    this.el.pwr = $('<div>Powered by <a href="https://godbolt.org">Compiler Explorer</a></div>')
                        .addClass('coliru-powered')
                        .hide().appendTo(this.el.action_bar);

    // find and store the original code and output divs
    orig_code = this.el.root.children('.mw-geshi:first, .mw-highlight:first');
    if (orig_code.length > 0) {
        this.has_orig_code = true;
        this.el.orig_code = orig_code.first();
    } else {
        this.has_orig_code = false;
        this.el.orig_code = $('<div/>').hide().insertAfter(this.el.action_bar);
    }

    orig_output = this.el.root.children('.mw-geshi:last, .mw-highlight:last');
    if (orig_output.length > 0 && !orig_output.is(orig_code)) {
        this.has_orig_output = true;
        this.el.orig_output = orig_output.first();
    } else {
        this.has_orig_output = false;
        this.el.output_p = $('<p>Output:</p>').hide().insertAfter(this.el.orig_code);
        this.el.orig_output = $('<div/>').hide().insertAfter(this.el.output_p);
    }

    if (this.has_orig_code) {
        this.source_code = this.el.orig_code.text().replace(/ /g, " ");
    } else {
        this.source_code = '';
    }

    // initialize the editor
    this.el.edited_code = $('<div/>').addClass('t-example-code mw-geshi')
                                     .hide().insertAfter(this.el.orig_code);
    this.el.editor_div = $('<div/>').addClass('ace_editor ace-tm')
                                    .appendTo(this.el.edited_code);
    this.editor = ace.edit(this.el.editor_div.get(0));
    this.editor.setTheme("ace/theme/textmate");
    this.editor.getSession().setMode("ace/mode/c_cpp");

    this.editor.commands.bindKeys({"Ctrl-l":null});
    this.editor.commands.bindKeys({"Ctrl-t":null});
    this.editor.commands.bindKeys({"Command-l":null});
    this.editor.commands.addCommand({
        name: 'Build and run',
        bindKey: {win: 'Ctrl-B', mac: 'Command-B'},
        exec: function(editor) { this.compile_now(); }.bind(this),
        readOnly: true
    });
    this.editor.commands.addCommand({
        name: 'Disable Ctrl-S',
        bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
        exec: function(editor) {},
        readOnly: true
    });

    this.editor.getSession().setValue(this.source_code);

    // create compiler output block
    this.el.compiler_output_p = $('<p>Compiler messages:</p>')
                                  .hide().insertAfter(this.el.edited_code);
    this.el.compiler_output = $('<div/>').addClass('coliru-output coliru-output-compiler mw-geshi')
                                         .hide().insertAfter(this.el.compiler_output_p);
    this.el.compiler_output_div = $('<div/>').appendTo(this.el.compiler_output);

    // create output block
    this.el.edited_output = $('<div/>').addClass('coliru-output coliru-output-program mw-geshi')
                                       .hide().insertAfter(this.el.orig_output);
    this.el.output_div = $('<div/>').appendTo(this.el.edited_output);

    this.reset_editor = function() {
        this.editor.getSession().setValue(this.source_code);
    };

    this.el.run_btn.click(function() {
        if (!this.el.run_btn.hasClass('coliru-btn-disabled')) {
            this.compile_now();
        }
    }.bind(this));
    this.el.share_btn.click(function() {
        if (!this.el.share_btn.hasClass('coliru-btn-disabled')) {
            this.share();
        }
    }.bind(this));
    this.el.exit_btn.click(function() {
        if (!this.el.exit_btn.hasClass('coliru-btn-disabled')) {
            this.restore_orig();
        }
    }.bind(this));

    this.enable_ui = function(value) {
        this.editor.setReadOnly(!value);

        if (value) {
            this.el.run_btn.removeClass('coliru-btn-disabled');
            this.el.share_btn.removeClass('coliru-btn-disabled');
            this.el.exit_btn.removeClass('coliru-btn-disabled');
        } else {
            this.el.run_btn.addClass('coliru-btn-disabled');
            this.el.share_btn.addClass('coliru-btn-disabled');
            this.el.exit_btn.addClass('coliru-btn-disabled');
        }
    };

    this.get_compiler = function() {
        return this.compiler_list[parseInt(this.el.cc_select.val())];
    };

    this.send_compile = function(src, compiler, f) {
        this.enable_ui(false);
        var parent = this;

        var http_request = new XMLHttpRequest();
        http_request.open('POST',
            'https://godbolt.org/api/compiler/' + compiler.compilerId + '/compile', true);
        http_request.setRequestHeader('Content-Type', 'application/json');
        http_request.setRequestHeader('Accept', 'application/json');
        http_request.onreadystatechange = function() {
            if (http_request.readyState === 4) {
                parent.enable_ui(true);
                if (http_request.status === 200) {
                    f(JSON.parse(http_request.responseText));
                }
            }
        };
        var common = (compiler.commonArgs !== undefined) ? compiler.commonArgs : parent.common_args;
        http_request.send(JSON.stringify({
            source: src,
            options: {
                userArguments: compiler.userArgs + (common ? ' ' + common : ''),
                compilerOptions: { },
                filters: { execute: true },
                executeParameters: { args: '', stdin: '' },
                libraries: [{ id: 'boost', version: '190' }]
            },
            lang: parent.lang
        }));
    };

    this.send_share = function(src, compiler, f) {
        this.enable_ui(false);
        var parent = this;

        var http_request = new XMLHttpRequest();
        http_request.open('POST', 'https://godbolt.org/api/shortener', true);
        http_request.setRequestHeader('Content-Type', 'application/json');
        http_request.setRequestHeader('Accept', 'application/json');
        http_request.onreadystatechange = function() {
            if (http_request.readyState === 4) {
                parent.enable_ui(true);
                if (http_request.status === 200) {
                    f(JSON.parse(http_request.responseText));
                }
            }
        };
        var common = (compiler.commonArgs !== undefined) ? compiler.commonArgs : parent.common_args;
        http_request.send(JSON.stringify({
            sessions: [{
                id: 1,
                language: parent.lang,
                source: src,
                compilers: [{
                    id: compiler.compilerId,
                    options: compiler.userArgs + (common ? ' ' + common : ''),
                    filters: { execute: true },
                    libs: [{ id: 'boost', version: '190' }]
                }],
                executors: []

            }]
        }));
    };

    this.compile_now = function() {
        var parent = this;
        this.editor.getSession().clearAnnotations();
        this.el.compiler_output_p.fadeOut('fast');
        this.el.compiler_output.fadeOut('fast');

        this.el.output_div.html('Building and running...');

        var compiler = this.get_compiler();
        var src = this.editor.getValue();

        this.send_compile(src, compiler, function(data) {
            var annotations = [];
            var annotation_lines = [];
            var compiler_output = '';
            var program_output = '';

            function strip_ansi(s) {
                return s.replace(/\x1b\[[0-9;]*[mKGHF]/g, '');
            }

            // compiler messages from stderr (compile phase)
            // and execResult.buildResult.stderr (link phase)
            var build_stderr = (data.stderr || []).concat(
                (   data.execResult 
                 && data.execResult.buildResult
                 && data.execResult.buildResult.stderr) || []);
            for (var i = 0; i < build_stderr.length; ++i) {
                var entry = build_stderr[i];
                var line = strip_ansi(entry.text || '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                var tag = entry.tag;

                var pre_open = '<pre';
                if (tag && tag.line) {
                    var error_lineno = tag.line;
                    var error_text = tag.text || '';
                    var error_type = (tag.severity === 'warning') ? 'warning' : 'error';

                    if (error_type !== 'note' && annotation_lines.indexOf(error_lineno) === -1) {
                        annotation_lines.push(error_lineno);
                        annotations.push({
                            row: error_lineno - 1,
                            column: 0,
                            text: error_text,
                            type: error_type
                        });
                    }

                    pre_open += ' onclick="window.jump_to_error(this, ' + error_lineno + ')"'
                             + ' onmouseover="window.highlight_error(this, true)"'
                             + ' onmouseout="window.highlight_error(this, false)"';
                }
                pre_open += '>';
                compiler_output += pre_open + line + '</pre>';
            }

            // program stdout
            var exec_result = data.execResult || {};
            var prog_stdout = exec_result.stdout || [];
            for (var j = 0; j < prog_stdout.length; ++j) {
                var pline = strip_ansi(prog_stdout[j].text || '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                program_output += '<pre class="coliru-output-line-exe">' + pline + '</pre>';
            }
            // program stderr (runtime errors, segfaults, etc.)
            var prog_stderr = exec_result.stderr || [];
            for (var k = 0; k < prog_stderr.length; ++k) {
                var sline = strip_ansi(prog_stderr[k].text || '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                program_output += '<pre class="coliru-output-line-shell">' + sline + '</pre>';
            }

            parent.el.output_div.html(program_output);

            if (compiler_output !== '') {
                parent.el.compiler_output_div.html(compiler_output);
                parent.el.compiler_output_p.fadeIn('fast');
                parent.el.compiler_output.fadeIn('fast');
            }

            window.setTimeout(function() {
                parent.editor.getSession().setAnnotations(annotations);
            }, 100);
        });
    };

    this.share = function() {
        var compiler = this.get_compiler();
        var src = this.editor.getValue();

        this.send_share(src, compiler, function(data) {
            if (data.url) {
                window.open(data.url, '_blank');
            }
        });
    };

    this.replace_orig = function() {
        if (!this.is_orig) {
            return;
        }

        this.el.root.height(this.el.root.height());

        var parent = this;
        this.el.run_init_btn.fadeOut("fast", function() {
            parent.el.run_btn.fadeIn("fast").css("display","inline-block");
            parent.el.share_btn.fadeIn("fast").css("display","inline-block");
            parent.el.exit_btn.fadeIn("fast").css("display","inline-block");
            parent.el.cc_select_div.fadeIn("fast").css("display","inline-block");
            parent.el.pwr.fadeIn("fast").css("display","inline-block");
        });

        this.el.orig_code.fadeOut("fast", function() {
            parent.el.edited_code.fadeIn("fast");
        });
        this.el.orig_output.fadeOut("fast", function() {
            if (!parent.has_orig_output) {
                parent.el.output_p.fadeIn('fast');
            }
            parent.el.edited_output.fadeIn("fast", function() {
                parent.el.root.height('auto');
            });
        });

        this.is_orig = false;
    };

    this.restore_orig = function() {
        if (this.is_orig) {
            return;
        }

        this.el.root.height(this.el.root.height());

        var parent = this;

        this.el.run_btn.fadeOut("fast", function() {
            parent.el.run_init_btn.fadeIn("fast").css("display","inline-block");
        });
        this.el.share_btn.fadeOut("fast");
        this.el.exit_btn.fadeOut("fast");
        this.el.cc_select_div.fadeOut("fast");
        this.el.pwr.fadeOut("fast");

        this.el.edited_code.fadeOut("fast", function() {
            parent.el.orig_code.fadeIn("fast");
        });

        this.el.compiler_output_p.fadeOut('fast');
        this.el.compiler_output.fadeOut('fast');

        if (!this.has_orig_output) {
            this.el.output_p.fadeOut('fast');
        }

        this.el.edited_output.fadeOut("fast", function() {
            parent.el.orig_output.fadeIn("fast", function() {
                parent.el.root.height('auto');
            });
        });

        this.is_orig = true;
    };

    this.enable_ui(true);
};

function get_script_cached(url, callback) {
    return $.ajax({
            type: "GET",
            url: url,
            success: callback,
            dataType: "script",
            cache: true
    });
};

window.highlight_error = function(node, b) {
    node.style.cursor = b ? 'pointer' : 'auto';
};

window.jump_to_error = function(node, lineno) {
    var root = $(node).parents('.t-example');
    if (root.length > 0) {
        for (var i = 0; i < editors.length; ++i) {
            if (editors[i].el.root.is(root)) {
                var editor = editors[i];
                editor.editor.gotoLine(lineno, 0, true);
                editor.editor.focus();
            }
        }
    }
};

var editors = [];

$.when(
    get_script_cached('https://cdn.jsdelivr.net/ace/1.2.4/noconflict/ace.js')
).done(function() {
    $.when(
        get_script_cached('https://cdn.jsdelivr.net/ace/1.2.4/noconflict/mode-c_cpp.js'),
        $.Deferred(function(deferred) {
            $(deferred.resolve);
        })
    ).done(function() {
        $('.t-example-live-link > a').each(function() {
            $(this).replaceWith('<div class="coliru-btn coliru-btn-run-init">Run this code</div>');
        });
        $('.t-example > .mw-geshi:first, .t-example > .mw-highlight:first').each(function() {
            $(this).addClass('t-example-code');
        });

        $('.coliru-btn-run-init').click(function() {
            var root = $(this).parent().parent();

            var i;
            for (i = 0; i < editors.length; ++i) {
                if (editors[i].el.root.is(root)) {
                    break;
                }
            }
            if (i == editors.length) {
                editors[i] = new Editor(root);
            }

            var editor = editors[i];
            editor.replace_orig();
            editor.compile_now();
        });
    });
});