Browse Source

Merge branch 'master' into execution

dev/git-series/gccdum
Matt Godbolt 6 years ago
parent
commit
33b801ee76
  1. 6
      .idea/codeStyleSettings.xml
  2. 3
      .travis.yml
  3. 10
      Makefile
  4. 3
      README.md
  5. 102
      app.js
  6. 33
      etc/config/c++.amazon.properties
  7. 21
      etc/config/c++.danger.properties
  8. 2
      etc/config/compiler-explorer.lud-mgodbolt01.properties
  9. 3
      etc/config/d.amazon.properties
  10. 6
      etc/config/rust.amazon.properties
  11. 6
      lib/compilers/ldc.js
  12. 73
      lib/exec.js
  13. 15
      lib/properties.js
  14. 5
      lib/utils.js
  15. 7
      package.json
  16. 25
      static/asm-mode.js
  17. 2
      static/assets/clippy.svg
  18. 36
      static/async-get.js
  19. 84
      static/compiler.js
  20. 69
      static/editor.js
  21. 75
      static/embed.html
  22. 10
      static/explorer.css
  23. 392
      static/index.html
  24. 3
      static/local.js
  25. 24
      static/main.js
  26. 4
      static/options.js
  27. 838
      static/rison.js
  28. 4
      static/rust-mode.js
  29. 1
      static/settings.js
  30. 92
      static/sharing.js
  31. 6
      static/thanks.html
  32. 2
      static/urlshorten-google.js
  33. 6
      test/embedding.html
  34. 15
      views/embed.pug
  35. 4
      views/example.cpp
  36. 4
      views/example.d
  37. 9
      views/example.go
  38. 4
      views/example.rs
  39. 7
      views/font-size.pug
  40. 6
      views/head.pug
  41. 99
      views/index.pug
  42. 89
      views/popups.pug
  43. 53
      views/templates.pug

6
.idea/codeStyleSettings.xml

@ -6,6 +6,12 @@
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="72" />
</MarkdownNavigatorCodeStyleSettings>
<codeStyleSettings language="Jade">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />

3
.travis.yml

@ -3,4 +3,5 @@ sudo: false
language: node_js
node_js:
- 6
- "node"

10
Makefile

@ -41,7 +41,8 @@ endif
NODE_MODULES=.npm-updated
$(NODE_MODULES): package.json
$(NPM) install
$(NPM) install --only=production
$(NPM) install --only=dev
@touch $@
BOWER_MODULES=.bower-updated
@ -79,13 +80,8 @@ dist: prereqs
$(NODE) ./node_modules/requirejs/bin/r.js -o app.build.js
# Move all assets to a versioned directory
mkdir -p out/dist/v/$(HASH)
# main.js
mv out/dist/main.js* out/dist/v/$(HASH)/
sed -i -e 's!data-main="main"!data-main="v/'"$(HASH)"'/main"'! out/dist/*.html
# explorer.css
mv out/dist/explorer.css out/dist/v/$(HASH)/
sed -i -e 's!href="explorer.css"!href="v/'"$(HASH)"'/explorer.css"'! out/dist/*.html
# any actual assets
mv out/dist/assets/ out/dist/v/$(HASH)/
# copy any external references into the directory too
cp -r $(shell pwd)/out/dist/ext out/dist/v/$(HASH)/ext
@ -97,8 +93,6 @@ dist: prereqs
--source-map-url require.js.map \
--source-map-root //v/$(HASH)/ext/requirejs \
--prefix 6
# rewrite any src refs
sed -i -e 's!src="!src="v/'"$(HASH)"'/'! out/dist/*.html
c-preload:
$(MAKE) -C c-preload

3
README.md

@ -40,7 +40,8 @@ There's now a [Road map](Roadmap.md) that gives a little insight into future pla
### Credits
Compiler Explorer is maintained by [Matt Godbolt](http://xania.org). Multiple compiler and difference view was
implemented by [Gabriel Devillers](https://github.com/voxelf).
implemented by [Gabriel Devillers](https://github.com/voxelf). [Rubén](https://github.com/RabsRincon)
contributed the auto-highlight between editors.
### RESTful API

102
app.js

@ -52,7 +52,7 @@ var opts = nopt({
'propDebug': [Boolean],
'debug': [Boolean],
'static': [String],
'archivedVersions': [String]
'archivedVersions': [String],
});
if (opts.debug) logger.level = 'debug';
@ -66,6 +66,9 @@ var port = opts.port || 10240;
var staticDir = opts.static || 'static';
var archivedVersions = opts.archivedVersions;
var gitReleaseName = child_process.execSync('git rev-parse HEAD').toString().trim();
var versionedRootPrefix = "";
if (opts.static && fs.existsSync(opts.static + '/v/' + gitReleaseName))
versionedRootPrefix = "v/" + gitReleaseName + "/";
var propHierarchy = _.flatten([
'defaults',
@ -101,6 +104,11 @@ function compilerProps(property, defaultValue) {
return gccProps(property, defaultValue); // gccProps comes from lib/compile-handler.js
}
var staticMaxAgeSecs = gccProps('staticMaxAgeSecs', 0);
function staticHeaders(res) {
if (staticMaxAgeSecs) {
res.setHeader('Cache-Control', 'public, max-age=' + staticMaxAgeSecs + ', must-revalidate');
}
}
var awsProps = props.propsFor("aws");
var awsPoller = null;
@ -151,40 +159,43 @@ function ClientOptionsHandler(fileSources) {
});
// sort source file alphabetically
sources = sources.sort(compareOn("name"));
var text = "";
var languages = _.map(gccProps("languages", '').split(':'), function (thing) {
var languages = _.compact(_.map(gccProps("languages", '').split(':'), function (thing) {
if (!thing) return null;
var splat = thing.split("=");
return {language: splat[0], url: splat[1]};
});
}));
var options = {
googleAnalyticsAccount: gccProps('clientGoogleAnalyticsAccount', 'UA-55180-6'),
googleAnalyticsEnabled: gccProps('clientGoogleAnalyticsEnabled', false),
sharingEnabled: gccProps('clientSharingEnabled', true),
githubEnabled: gccProps('clientGitHubRibbonEnabled', true),
gapiKey: gccProps('googleApiKey', ''),
googleShortLinkRewrite: gccProps('googleShortLinkRewrite', '').split('|'),
defaultSource: gccProps('defaultSource', ''),
language: language,
compilers: [],
sourceExtension: compilerProps('compileFilename').split('.', 2)[1],
defaultCompiler: compilerProps('defaultCompiler', ''),
compileOptions: compilerProps("options"),
supportsBinary: !!compilerProps("supportsBinary"),
languages: languages,
sources: sources,
raven: gccProps('ravenUrl', ''),
release: gitReleaseName,
environment: env,
localStoragePrefix: gccProps('localStoragePrefix')
};
this.setCompilers = function (compilers) {
var options = {
googleAnalyticsAccount: gccProps('clientGoogleAnalyticsAccount', 'UA-55180-6'),
googleAnalyticsEnabled: gccProps('clientGoogleAnalyticsEnabled', false),
sharingEnabled: gccProps('clientSharingEnabled', true),
githubEnabled: gccProps('clientGitHubRibbonEnabled', true),
gapiKey: gccProps('googleApiKey', ''),
googleShortLinkRewrite: gccProps('googleShortLinkRewrite', '').split('|'),
defaultSource: gccProps('defaultSource', ''),
language: language,
compilers: compilers,
sourceExtension: compilerProps('compileFilename').split('.', 2)[1],
defaultCompiler: compilerProps('defaultCompiler', ''),
compileOptions: compilerProps("options"),
supportsBinary: !!compilerProps("supportsBinary"),
languages: languages,
sources: sources,
raven: gccProps('ravenUrl', ''),
release: gitReleaseName,
environment: env,
localStoragePrefix: gccProps('localStoragePrefix')
};
text = JSON.stringify(options);
options.compilers = compilers;
};
this.setCompilers([]);
this.handler = function getClientOptions(req, res) {
res.set('Content-Type', 'application/json');
res.set('Cache-Control', 'public, max-age=' + staticMaxAgeSecs);
res.end(text);
staticHeaders(res);
res.end(JSON.stringify(options));
};
this.get = function () {
return options;
};
}
@ -206,7 +217,7 @@ function getSource(req, res, next) {
return;
}
action.apply(handler, bits.slice(3).concat(function (err, response) {
res.set('Cache-Control', 'public, max-age=' + staticMaxAgeSecs);
staticHeaders(res);
if (err) {
res.end(JSON.stringify({err: err}));
} else {
@ -452,15 +463,6 @@ function shortUrlHandler(req, res, next) {
});
}
// TODO: write the info here directly instead of redirecting...
function embeddedHandler(req, res, next) {
res.writeHead(301, {
Location: 'embed.html',
'Cache-Control': 'public'
});
res.end();
}
findCompilers()
.then(function (compilers) {
var prevCompilers;
@ -500,10 +502,33 @@ findCompilers()
logger.info(" serving static files from '" + staticDir + "'");
logger.info(" git release " + gitReleaseName);
function renderConfig(extra) {
var options = _.extend(extra, clientOptionsHandler.get());
options.compilerExplorerOptions = JSON.stringify(options);
options.root = versionedRootPrefix;
return options;
}
var embeddedHandler = function (req, res) {
staticHeaders(res);
res.render('embed', renderConfig({embedded: true}));
};
webServer
.set('trust proxy', true)
.set('view engine', 'pug')
.set('view cache', true)
.use(morgan('combined', {stream: logger.stream}))
.use(compression())
.get('/', function (req, res) {
staticHeaders(res);
res.render('index', renderConfig({embedded: false}));
})
.get('/e', embeddedHandler)
.get('/embed.html', embeddedHandler) // legacy. not a 301 to prevent any redirect loops between old e links and embed.html
.get('/embed-ro', function (req, res) {
staticHeaders(res);
res.render('embed', renderConfig({embedded: true, readOnly: true}));
})
.use(sFavicon(staticDir + '/favicon.ico'))
.use('/v', express.static(staticDir + '/v', {maxAge: Infinity, index: false}))
.use(express.static(staticDir, {maxAge: staticMaxAgeSecs * 1000}));
@ -525,7 +550,6 @@ findCompilers()
.use('/source', getSource)
.use('/api', apiHandler.handler)
.use('/g', shortUrlHandler)
.use('/e', embeddedHandler)
.post('/compile', compileHandler.handler);
logger.info("=======================================");

33
etc/config/c++.amazon.properties

@ -1,4 +1,4 @@
compilers=gcc1204@20480:&gcc86:&icc:&clang:&cl:&cross:&ellcc
compilers=gcc1204@20480:&gcc86:&icc:&clang:&cl:&cross:&ellcc:&zapcc
defaultCompiler=g63
textBanner=Compilation provided by Compiler Explorer at https://gcc.godbolt.org/
@ -55,9 +55,9 @@ compiler.g7snapshot.exe= /opt/compiler-explorer/gcc-7-snapshot/bin/g++
compiler.g7snapshot.name=x86-64 gcc 7 (snapshot)
# Clang for x86
group.clang.compilers=clang30:clang31:clang32:clang33:clang341:clang350:clang351:clang37x:clang36x:clang371:clang380:clang381:clang390:clang391:clang_trunk
group.clang.compilers=clang30:clang31:clang32:clang33:clang341:clang350:clang351:clang37x:clang36x:clang371:clang380:clang381:clang390:clang391:clang400:clang_trunk
group.clang.intelAsm=-mllvm --x86-asm-syntax=intel
group.clang.options=--gcc-toolchain=/opt/compiler-explorer/gcc-6.2.0
group.clang.options=--gcc-toolchain=/opt/compiler-explorer/gcc-6.3.0
compiler.clang30.exe=/opt/compiler-explorer/clang+llvm-3.0-x86_64-linux-Ubuntu-11_10/bin/clang++
compiler.clang30.alias=/usr/bin/clang++
compiler.clang30.name=x86-64 clang 3.0
@ -96,6 +96,8 @@ compiler.clang390.exe=/opt/compiler-explorer/clang+llvm-3.9.0-x86_64-linux-gnu-u
compiler.clang390.name=x86-64 clang 3.9.0
compiler.clang391.exe=/opt/compiler-explorer/clang-3.9.1/bin/clang++
compiler.clang391.name=x86-64 clang 3.9.1
compiler.clang400.exe=/opt/compiler-explorer/clang-4.0.0/bin/clang++
compiler.clang400.name=x86-64 clang 4.0.0
compiler.clang_trunk.exe=/opt/compiler-explorer/clang-trunk/bin/clang++
compiler.clang_trunk.name=x86-64 clang (trunk)
@ -113,9 +115,16 @@ compiler.icc16.name=x86-64 icc 16
compiler.icc17.exe=/opt/compiler-explorer/intel/2017/bin/icc
compiler.icc17.name=x86-64 icc 17
# zapcc
group.zapcc.compilers=zapcc190308
group.zapcc.intelAsm=-mllvm --x86-asm-syntax=intel
group.zapcc.options=--gcc-toolchain=/opt/compiler-explorer/gcc-6.3.0
compiler.zapcc190308.exe=/opt/compiler-explorer/zapcc-20170226-190308-1.0/bin/zapcc++
compiler.zapcc190308.name=Zapcc 190308
###############################
# GCC for ppc
group.cross.compilers=ppcg48:aarchg54:armhfg54:mips5:mips5el:mips564:mips564el:msp430g530:arm541
group.cross.compilers=ppcg48:aarchg54:armhfg54:mips5:mips5el:mips564:mips564el:msp430g530:msp430g621:arm541
group.cross.needsMulti=false
group.cross.supportsBinary=false
compiler.ppcg48.exe=/usr/bin/powerpc-linux-gnu-g++-4.8
@ -147,6 +156,8 @@ compiler.mips564el.name=MIPS64 gcc 5.4 (el)
# MSP
compiler.msp430g530.exe=/opt/compiler-explorer/msp430-gcc-5.3.0.219_linux32/bin/msp430-elf-g++
compiler.msp430g530.name=MSP430 gcc 5.3.0
compiler.msp430g621.exe=/opt/compiler-explorer/msp430-gcc-6.2.1.16_linux64/bin/msp430-elf-g++
compiler.msp430g621.name=MSP430 gcc 6.2.1
################################
# Windows Compilers
@ -156,13 +167,13 @@ group.cl.compilerType=CL
group.cl.versionFlag=/?
group.cl.versionRe=^Microsoft \(R\) C/C\+\+.*$
group.cl19.compilers=cl19_64:cl19_32:cl19_arm
group.cl19.options=/I/opt/compiler-explorer/windows/10.0.10240.0/ucrt/ /I/opt/compiler-explorer/windows/14.0.24629/lib/native/include/
compiler.cl19_64.exe=/opt/compiler-explorer/windows/14.0.24629/lib/native/bin/amd64/cl.exe
compiler.cl19_64.name=x86-64 CL 19 RC
compiler.cl19_32.exe=/opt/compiler-explorer/windows/14.0.24629/lib/native/bin/amd64_x86/cl.exe
compiler.cl19_32.name=x86 CL 19 RC
compiler.cl19_arm.exe=/opt/compiler-explorer/windows/14.0.24629/lib/native/bin/amd64_arm/cl.exe
compiler.cl19_arm.name=ARM CL 19 RC
group.cl19.options=/I/opt/compiler-explorer/windows/10.0.10240.0/ucrt/ /I/opt/compiler-explorer/windows/19.10.25017/lib/native/include/
compiler.cl19_64.exe=/opt/compiler-explorer/windows/19.10.25017/lib/native/bin/amd64/cl.exe
compiler.cl19_64.name=x86-64 CL 19 2017 RTW
compiler.cl19_32.exe=/opt/compiler-explorer/windows/19.10.25017/lib/native/bin/amd64_x86/cl.exe
compiler.cl19_32.name=x86 CL 19 2017 RTW
compiler.cl19_arm.exe=/opt/compiler-explorer/windows/19.10.25017/lib/native/bin/amd64_arm/cl.exe
compiler.cl19_arm.name=ARM CL 2017 RTW
#################################
# ELLCC

21
etc/config/c++.danger.properties

@ -1,6 +1,7 @@
# Matt's home development computer
defaultCompiler=g54
textBanner=Testing the text banner
compilers=&cl:g54:&clang
#compilers=g54:g47:g48:avr
#compilers=localhost@20480
compiler.g54.exe=/usr/bin/g++
@ -12,3 +13,23 @@ compiler.g48.name=g++ 4.8
compiler.avr.exe=/usr/bin/avr-g++
compiler.avr.name=g++ avr
#compiler-wrapper=./c-preload/compiler-wrapper
group.cl.compilers=&cl19
group.cl.needsMulti=false
group.cl.compilerType=CL
group.cl.versionFlag=/?
group.cl.versionRe=^Microsoft \(R\) C/C\+\+.*$
group.cl19.compilers=cl19_64:cl19_32:cl19_arm
group.cl19.options=/I/opt/compiler-explorer/windows/10.0.10240.0/ucrt/ /I/opt/compiler-explorer/windows/19.10.25017/lib/native/include/
compiler.cl19_64.exe=/opt/compiler-explorer/windows/19.10.25017/lib/native/bin/amd64/cl.exe
compiler.cl19_64.name=x86-64 CL 19 2017 RTW
compiler.cl19_32.exe=/opt/compiler-explorer/windows/19.10.25017/lib/native/bin/amd64_x86/cl.exe
compiler.cl19_32.name=x86 CL 19 2017 RTW
compiler.cl19_arm.exe=/opt/compiler-explorer/windows/19.10.25017/lib/native/bin/amd64_arm/cl.exe
compiler.cl19_arm.name=ARM CL 2017 RTW
group.clang.compilers=clang391
group.clang.intelAsm=-mllvm --x86-asm-syntax=intel
group.clang.options=--gcc-toolchain=/opt/compiler-explorer/gcc-6.3.0
compiler.clang391.exe=/opt/compiler-explorer/clang-3.9.1/bin/clang++
compiler.clang391.name=x86-64 clang 3.9.1

2
etc/config/compiler-explorer.lud-mgodbolt01.properties

@ -2,4 +2,4 @@ language=C++
clientURLShortener=google
rescanCompilerSecs=360
ravenUrl=https://8e4614f649ad4e3faf3e7e8827b935f9@sentry.io/102028
languages=C++=gcc:D=d:Rust=rust:Go=go
#languages=C++=gcc:D=d:Rust=rust:Go=go

3
etc/config/d.amazon.properties

@ -1,8 +1,7 @@
compilers=gdc48:gdc49:gdc52:&ldc
textBanner=Compilation provided by Compiler Explorer at https://d.godbolt.org/
defaultCompiler=gdc52
# This is only due to limitations on the d.godbolt.org image for now.
supportsBinary=false
compiler.gdc49.exe=/opt/compiler-explorer/gdc4.9.3/x86_64-pc-linux-gnu/bin/gdc
compiler.gdc49.alias=/opt/x86_64-gdcproject-linux-gnu/bin/gdc
compiler.gdc49.name=gdc 4.9.3

6
etc/config/rust.amazon.properties

@ -1,8 +1,10 @@
compilers=&rust
textBanner=Compilation provided by Compiler Explorer at https://rust.godbolt.org/
defaultCompiler=r1151
group.rust.compilers=r1151:r1140:r1130:r1120:r1110:r1100:r190:r180:r170:r160:r150:r140:r130:r120:r110:r100:nightly:beta
defaultCompiler=r1160
group.rust.compilers=r1160:r1151:r1140:r1130:r1120:r1110:r1100:r190:r180:r170:r160:r150:r140:r130:r120:r110:r100:nightly:beta
group.rust.compilerType=rust
compiler.r1160.exe=/opt/compiler-explorer/rust-1.16.0/bin/rustc
compiler.r1160.name=rustc 1.16.0
compiler.r1151.exe=/opt/compiler-explorer/rust-1.15.1/bin/rustc
compiler.r1151.name=rustc 1.15.1
compiler.r1140.exe=/opt/compiler-explorer/rust-1.14.0/bin/rustc

6
lib/compilers/ldc.js

@ -26,14 +26,14 @@ var Compile = require('../base-compiler');
function compileLdc(info, env) {
var compiler = new Compile(info, env);
// TODO this needs testing!
compiler.compiler.supportsIntel = true;
compiler.optionsForFilter = function (filters, outputFilename) {
var options = ['-g', '-of', this.filename(outputFilename)];
if (filters.intel && !filters.binary) options.concat('-x86-asm-syntax=intel');
if (filters.intel && !filters.binary) options = options.concat('-x86-asm-syntax=intel');
if (!filters.binary) options = options.concat('-output-s');
return options;
};
return compiler.initialise();
}
module.exports = compileLdc;
module.exports = compileLdc;

73
lib/exec.js

@ -46,39 +46,46 @@ function execute(command, args, options) {
});
var running = true;
var killChild = options.killChild || function () {
var kill = options.killChild || function () {
if (running) treeKill(child.pid);
};
var stderr = "";
var stdout = "";
var streams = {
stderr: "",
stdout: "",
truncated: false
};
var timeout;
if (timeoutMs)
timeout = setTimeout(function () {
okToCache = false;
killChild();
stderr += "\nKilled - processing time exceeded";
}, timeoutMs);
var truncated = false;
child.stdout.on('data', function (data) {
if (truncated) return;
if (stdout.length > maxOutput) {
stdout += "\n[Truncated]";
truncated = true;
killChild();
return;
}
stdout += data;
});
child.stderr.on('data', function (data) {
if (truncated) return;
if (stderr.length > maxOutput) {
stderr += "\n[Truncated]";
truncated = true;
killChild();
return;
}
stderr += data;
});
if (timeoutMs) timeout = setTimeout(function () {
logger.warn("Timeout for", command, args, "after", timeoutMs, "ms");
okToCache = false;
kill();
streams.stderr += "\nKilled - processing time exceeded";
}, timeoutMs);
function setupOnError(stream, name) {
stream.on('error', function (err) {
logger.error('Error with ' + name + ' stream:', err);
});
}
function setupStream(stream, name) {
stream.on('data', function (data) {
if (streams.truncated) return;
if (streams[name].length > maxOutput) {
streams[name] += "\n[Truncated]";
streams.truncated = true;
kill();
return;
}
streams[name] += data;
});
setupOnError(stream, name);
}
setupOnError(child.stdin, 'stdin');
setupStream(child.stdout, 'stdout');
setupStream(child.stderr, 'stderr');
child.on('exit', function (code) {
logger.debug({type: 'exited', code: code});
if (timeout !== undefined) clearTimeout(timeout);
@ -93,8 +100,8 @@ function execute(command, args, options) {
if (timeout !== undefined) clearTimeout(timeout);
var result = {
code: code,
stdout: stdout,
stderr: stderr,
stdout: streams.stdout,
stderr: streams.stderr,
okToCache: okToCache
};
logger.debug({type: "executed", command: command, args: args, result: result});
@ -206,4 +213,4 @@ function sandbox(command, args, options) {
module.exports = {
execute: execute,
sandbox: sandbox
};
};

15
lib/properties.js

@ -23,7 +23,8 @@
// POSSIBILITY OF SUCH DAMAGE.
var fs = require('fs'),
logger = require('./logger').logger;
logger = require('./logger').logger,
_ = require('underscore-node');
var properties = {};
@ -81,8 +82,10 @@ function parseProperties(blob, name) {
function initialize(directory, hier) {
if (hier === null) throw new Error('Must supply a hierarchy array');
logger.info("Reading properties from " + directory + " with hierarchy " + hier);
hierarchy = hier;
hierarchy = _.map(hier, function (x) {
return x.toLowerCase();
});
logger.info("Reading properties from " + directory + " with hierarchy " + hierarchy);
var endsWith = /\.properties$/;
var propertyFiles = fs.readdirSync(directory).filter(function (filename) {
return filename.match(endsWith);
@ -98,7 +101,7 @@ function initialize(directory, hier) {
}
function propsFor(base) {
return function(property, defaultValue) {
return function (property, defaultValue) {
return get(base, property, defaultValue);
};
}
@ -107,5 +110,7 @@ module.exports = {
get: get,
propsFor: propsFor,
initialize: initialize,
setDebug: function(debug) { propDebug = debug; }
setDebug: function (debug) {
propDebug = debug;
}
};

5
lib/utils.js

@ -50,10 +50,9 @@ function expandTabs(line) {
exports.expandTabs = expandTabs;
function parseOutput(lines, inputFilename) {
var re = /^<source>[:(]([0-9]+)(:([0-9]+):)?[):]*\s*(.*)/;
var re = /^\s*<source>[:(]([0-9]+)(:([0-9]+):)?[):]*\s*(.*)/;
var result = [];
eachLine(lines, function (line) {
line = line.trim();
if (inputFilename) line = line.replace(inputFilename, '<source>');
if (line !== "" && line.indexOf("fixme:") !== 0) {
var lineObj = {text: line};
@ -78,4 +77,4 @@ function padRight(name, len) {
return name;
}
exports.padRight = padRight;
exports.padRight = padRight;

7
package.json

@ -23,16 +23,17 @@
"fs-extra": "0.26.x",
"http-proxy": "1.12.x",
"lru-cache": "2.7.x",
"monaco-editor": "0.8.x",
"monaco-editor": "0.8.3",
"morgan": "1.6.x",
"nopt": "3.0.x",
"promise": "7.0.x",
"promise-queue": "2.1.x",
"nopt": "3.0.x",
"pug": "^2.0.0-beta11",
"serve-favicon": "2.3.x",
"shell-quote": "1.6.x",
"temp": "0.8.x",
"tree-kill": "1.1.x",
"underscore-node": "*",
"shell-quote": "1.6.x",
"winston": "2.2.x"
},
"devDependencies": {

25
static/asm-mode.js

@ -31,13 +31,10 @@ define(function (require) {
// Set defaultToken to invalid to see what you do not tokenize yet
defaultToken: 'invalid',
// we include these common regular expressions
symbols: /[=><!~?:&|+\-*\/\^%]+/,
// C# style strings
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
registers: /%?\b(r[0-9]+|([er]?(ax|cx|dx|sp|bp|si|di))|[xyz]mm[0-9]+|sp|fp|lr)\b/,
registers: /%?\b(r[0-9]+[dbw]?|([er]?(a[xhl]|c[xhl]|d[xhl]|cs|fs|ds|ss|sp|bp|ip|sil?|dil?))|[xyz]mm[0-9]+|sp|fp|lr)\b/,
intelOperators: /PTR|(D|Q|[XYZ]MM)?WORD/,
@ -46,15 +43,15 @@ define(function (require) {
// Error document
[/^<.*>$/, {token: 'annotation'}],
// Label definition
[/^[.a-zA-Z0-9_$][^:]*:/, {token: 'type.identifier', next: '@rest'}],
[/^[.a-zA-Z0-9_$].*:/, {token: 'type.identifier', next: '@rest'}],
// Label definition (ARM style)
[/^\s*[|][^|]*[|]/, {token: 'type.identifier', next: '@rest'}],
// Label defintion (CL style)
// Label definition (CL style)
[/^\s*[.a-zA-Z0-9_$|]*\s*(PROC|ENDP)/, {token: 'type.identifier', next: '@rest'}],
// Constant definition
[/^[.a-zA-Z0-9_$][^=]*=/, {token: 'type.identifier', next: '@rest'}],
// opcode
[/[a-zA-Z]+/, {token: 'keyword', next: '@rest'}],
[/[.a-zA-Z_][.a-zA-Z_0-9]*/, {token: 'keyword', next: '@rest'}],
// whitespace
{include: '@whitespace'}
@ -66,9 +63,8 @@ define(function (require) {
[/@registers/, 'variable.predefined'],
[/@intelOperators/, 'annotation'],
// delimiters and operators
[/[{}()\[\]]/, '@brackets'],
[/[<>](?!@symbols)/, '@brackets'],
// brackets
[/[{}<>()\[\]]/, '@brackets'],
// ARM-style label reference
[/[|][^|]*[|]*/, 'type.identifier'],
@ -81,10 +77,10 @@ define(function (require) {
[/#-?\d+/, 'number'],
// operators
[/[-+,*\/!]/, 'operator'],
[/[-+,*\/!:&]/, 'operator'],
// strings
[/"([^"\\]|\\.)*$/, 'string.invalid'], // non-teminated string
[/"([^"\\]|\\.)*$/, 'string.invalid'], // non-terminated string
[/"/, {token: 'string.quote', bracket: '@open', next: '@string'}],
// characters
@ -93,7 +89,7 @@ define(function (require) {
[/'/, 'string.invalid'],
// Assume anything else is a label reference
[/[.?_$a-zA-Z][.?_$a-zA-Z0-9]*/, 'type.identifier'],
[/%?[.?_$a-zA-Z@][.?_$a-zA-Z0-9@]*/, 'type.identifier'],
// whitespace
{include: '@whitespace'}
@ -117,8 +113,7 @@ define(function (require) {
[/[ \t\r\n]+/, 'white'],
[/\/\*/, 'comment', '@comment'],
[/\/\/.*$/, 'comment'],
[/#.*$/, 'comment'],
[/@.*$/, 'comment']
[/[#;\\@].*$/, 'comment']
]
}
};

2
static/assets/clippy.svg

@ -1,3 +1,3 @@
<svg height="1024" width="896" xmlns="http://www.w3.org/2000/svg">
<path d="M128 768h256v64H128v-64z m320-384H128v64h320v-64z m128 192V448L384 640l192 192V704h320V576H576z m-288-64H128v64h160v-64zM128 704h160v-64H128v64z m576 64h64v128c-1 18-7 33-19 45s-27 18-45 19H64c-35 0-64-29-64-64V192c0-35 29-64 64-64h192C256 57 313 0 384 0s128 57 128 128h192c35 0 64 29 64 64v320h-64V320H64v576h640V768zM128 256h512c0-35-29-64-64-64h-64c-35 0-64-29-64-64s-29-64-64-64-64 29-64 64-29 64-64 64h-64c-35 0-64 29-64 64z" />
<path d="M128 768h256v64H128v-64z m320-384H128v64h320v-64z m128 192V448L384 640l192 192V704h320V576H576z m-288-64H128v64h160v-64zM128 704h160v-64H128v64z m576 64h64v128c-1 18-7 33-19 45s-27 18-45 19H64c-35 0-64-29-64-64V192c0-35 29-64 64-64h192C256 57 313 0 384 0s128 57 128 128h192c35 0 64 29 64 64v320h-64V320H64v576h640V768zM128 256h512c0-35-29-64-64-64h-64c-35 0-64-29-64-64s-29-64-64-64-64 29-64 64-29 64-64 64h-64c-35 0-64 29-64 64z"/>
</svg>

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 520 B

36
static/async-get.js

@ -1,36 +0,0 @@
// Copyright (c) 2012-2017, Matt Godbolt
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
define({
load: function (name, req, onLoad, config) {
if (config.isBuild) return onLoad(null); // optimizer build
req(['jquery'], function ($) {
$.ajax({type: "GET", url: name}).done(function (response) {
onLoad(response);
});
});
}
});

84
static/compiler.js

@ -71,6 +71,10 @@ define(function (require) {
this.lastResult = null;
this.pendingRequestSentAt = 0;
this.nextRequest = null;
this.settings = {};
this.decorations = {};
this.prevDecorations = [];
this.domRoot.find(".compiler-picker").selectize({
sortField: 'name',
@ -96,9 +100,25 @@ define(function (require) {
this.outputEditor = monaco.editor.create(this.domRoot.find(".monaco-placeholder")[0], {
scrollBeyondLastLine: false,
readOnly: true,
language: 'asm'
language: 'asm',
glyphMargin: true
});
this.outputEditor.addAction({
id: 'viewsource',
label: 'Highlight source',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10],
keybindingContext: null,
contextMenuGroupId: 'navigation',
contextMenuOrder: 1.5,
run: function (ed) {
var desiredLine = ed.getPosition().lineNumber - 1;
self.eventHub.emit('editorSetDecoration', self.sourceEditorId, self.assembly[desiredLine].source);
}
});
this.outputEditor.onMouseMove(_.throttle(_.bind(this.onMouseMove, this)), 250);
this.fontScale = new FontScale(this.domRoot, state, this.outputEditor);
this.fontScale.on('change', _.bind(function () {
this.saveState();
@ -122,6 +142,9 @@ define(function (require) {
this.eventHub.on('colours', this.onColours, this);
this.eventHub.on('resendCompilation', this.onResendCompilation, this);
this.eventHub.on('findCompilers', this.sendCompiler, this);
this.eventHub.on('compilerSetDecorations', this.onCompilerSetDecorations, this);
this.eventHub.on('settingsChange', this.onSettingsChange, this);
this.eventHub.emit('requestSettings');
this.sendCompiler();
this.updateCompilerName();
this.updateButtons();
@ -255,7 +278,7 @@ define(function (require) {
var decorations = [];
_.each(this.assembly, _.bind(function (obj, line) {
var address = obj.address ? obj.address.toString(16) : "";
// var div = $("<div class='address cm-number'>" + address + "</div>");
// var div = $("<div class='address cm-number'>" + address + "</div>");
addrToAddrDiv[address] = {div: "moo", line: line};
}, this));
@ -477,6 +500,63 @@ define(function (require) {
}
};
Compiler.prototype.updateDecorations = function () {
this.prevDecorations = this.outputEditor.deltaDecorations(
this.prevDecorations, _.flatten(_.values(this.decorations), true));
};
Compiler.prototype.onCompilerSetDecorations = function (id, lineNums) {
if (id == this.id) {
this.decorations.linkedCode = _.map(lineNums, function (line) {
return {
range: new monaco.Range(line, 1, line, 1),
options: {
linesDecorationsClassName: 'linked-code-decoration'
}
};
});
this.updateDecorations();
}
};
Compiler.prototype.onSettingsChange = function (newSettings) {
var lastHoverShowSource = this.settings.hoverShowSource;
this.settings = _.clone(newSettings);
if (!lastHoverShowSource && this.settings.hoverShowSource) {
this.onCompilerSetDecorations(this.id, []);
}
};
var hexLike = /^(#?[$]|0x)([0-9a-fA-F]+)$/;
var decimalLike = /^(#?)([0-9]+)$/;
function getToolTip(value) {
var match = hexLike.exec(value);
if (match) return value + ' = ' + parseInt(match[2], 16).toString();
match = decimalLike.exec(value);
if (match) return value + ' = 0x' + parseInt(match[2]).toString(16);
return null;
}
Compiler.prototype.onMouseMove = function (e) {
if (e === null || e.target === null || e.target.position === null) return;
if (this.settings.hoverShowSource === true && this.assembly) {
var desiredLine = e.target.position.lineNumber - 1;
if (this.assembly[desiredLine]) {
// We check that we actually have something to show at this point!
this.eventHub.emit('editorSetDecoration', this.sourceEditorId, this.assembly[desiredLine].source);
}
}
var numericToolTip = e.target.element ? getToolTip(e.target.element.textContent) : null;
if (numericToolTip) {
this.decorations.numericToolTip = {
range: e.target.range,
options: {isWholeLine: false, hoverMessage: ['`' + numericToolTip + '`']}
};
this.updateDecorations();
}
};
return {
Compiler: Compiler
};

69
static/editor.js

@ -33,6 +33,7 @@ define(function (require) {
var Sharing = require('sharing');
var Components = require('components');
var monaco = require('monaco');
var options = require('options');
require('./d-mode');
require('./rust-mode');
@ -52,6 +53,9 @@ define(function (require) {
this.asmByCompiler = {};
this.busyCompilers = {};
this.colours = [];
this.lastCompilerIDResponse = -1;
this.decorations = [];
var cmMode;
switch (lang.toLowerCase()) {
@ -73,21 +77,63 @@ define(function (require) {
}
var root = this.domRoot.find(".monaco-placeholder");
var legacyReadOnly = state.options && !!state.options.readOnly;
this.editor = monaco.editor.create(root[0], {
value: state.source || defaultSrc || "",
scrollBeyondLastLine: false,
language: cmMode
language: cmMode,
readOnly: !!options.readOnly || legacyReadOnly,
glyphMargin: true
});
this.editor.addAction({
id: 'compile',
label: 'Compile',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
keybindingContext: null,
contextMenuGroupId: 'navigation',
contextMenuOrder: 1.5,
run: _.bind(function () {
this.maybeEmitChange();
}, this)
});
function tryCompilerSelectLine(thisLineNumber) {
_.each(self.asmByCompiler, function (asms, compilerId) {
var targetLines = [];
_.each(asms, function (asmLine, i) {
if (asmLine.source == thisLineNumber) {
targetLines.push(i + 1);
}
});
self.eventHub.emit('compilerSetDecorations', compilerId, targetLines);
});
}
this.editor.addAction({
id: 'viewasm',
label: 'Highlight assembly',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10],
keybindingContext: null,
contextMenuGroupId: 'navigation',
contextMenuOrder: 1.5,
run: function (ed) {
tryCompilerSelectLine(ed.getPosition().lineNumber);
}
});
this.mouseMoveThrottledFunction = _.throttle(function (e) {
if (e !== null && e.target !== null && self.settings.hoverShowSource === true && e.target.position !== null) {
tryCompilerSelectLine(e.target.position.lineNumber);
}
},
250
);
this.editor.onMouseMove(function (e) {
self.mouseMoveThrottledFunction(e);
});
this.fontScale = new FontScale(this.domRoot, state, this.editor);
this.fontScale.on('change', _.bind(this.updateState, this));
@ -143,7 +189,7 @@ define(function (require) {
this.eventHub.on('compiling', this.onCompiling, this);
this.eventHub.on('compileResult', this.onCompileResponse, this);
this.eventHub.on('selectLine', this.onSelectLine, this);
this.eventHub.on('editorSetDecoration', this.onEditorSetDecoration, this);
this.eventHub.on('settingsChange', this.onSettingsChange, this);
this.eventHub.emit('requestSettings');
@ -207,6 +253,10 @@ define(function (require) {
}
}
if (before.hoverShowSource && !after.hoverShowSource) {
this.onEditorSetDecoration(this.id, -1);
}
this.numberUsedLines();
};
@ -276,6 +326,7 @@ define(function (require) {
}, this));
monaco.editor.setModelMarkers(this.editor.getModel(), compilerId, widgets);
this.asmByCompiler[compilerId] = result.asm;
this.lastCompilerIDResponse = compilerId;
this.numberUsedLines();
};
@ -285,6 +336,20 @@ define(function (require) {
}
};
Editor.prototype.onEditorSetDecoration = function (id, lineNum) {
if (id === this.id) {
this.decorations = this.editor.deltaDecorations(this.decorations,
lineNum === -1 || lineNum === null ? [] : [
{
range: new monaco.Range(lineNum, 1, lineNum, 1),
options: {
linesDecorationsClassName: 'linked-code-decoration'
}
}
]);
}
};
return {
Editor: Editor
};

75
static/embed.html

@ -1,75 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Compiler Explorer</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="explorer.css" rel="stylesheet">
<script data-main="main" src="ext/requirejs/require.js"></script>
</head>
<body class="embedded">
<a href="/" class="float-link link" target="_blank">
Edit on <span class="language-name"></span> Compiler Explorer
<span class="glyphicon glyphicon-new-window"></span><br/>
</a>
<div id="root"></div>
<div class="gl_keep template">
<div id="codeEditor">
<div class="monaco-placeholder"></div>
</div>
<div id="compiler">
<div class="top-bar">
<table>
<tr>
<td><select class="compiler-picker" placeholder="Select a compiler..."></select></td>
<td><input class="options form-control" type="text" placeholder="compiler options..."
size="256"></td>
</tr>
</table>
<div class="btn-group btn-group-sm filters hidden" data-toggle="buttons">
<button class="btn btn-sm" title="Compile to binary and disassemble the output"
data-bind="binary">
<span>11010</span>
</button>
<button class="btn btn-sm active nonbinary" title="Filter unused labels from the output"
data-bind="labels">
<span>.LX0:</span>
</button>
<button class="btn btn-sm active nonbinary" title="Filter all assembler directives from the output"
data-bind="directives">
<span>.text</span>
</button>
<button class="btn btn-sm active nonbinary"
title="Remove all lines which are only comments from the output"
data-bind="commentOnly">
<span>//</span>
</button>
<button class="btn btn-sm active" title="Output disassembly in Intel syntax"
data-bind="intel">
<span>Intel</span>
</button>
</div>
</div>
<div class="monaco-placeholder"></div>
<div class="bottom-bar">
<div class="btn-group btn-group-sm">
<button><span class="glyphicon glyphicon-alert status"></span></button>
</div>
<span class="full-compiler-name"></span>
<span class="compile-time"></span>
</div>
</div>
<div id="compiler-output">
<pre class="content"></pre>
</div>
</div>
</body>
</html>

10
static/explorer.css

@ -6,7 +6,9 @@
@import url("colours.css");
/* TEMP HACK: https://github.com/Microsoft/monaco-editor/issues/349 */
.quick-open-tree .row { margin-left: 0; }
.quick-open-tree .row {
margin-left: 0;
}
.navbar {
border-radius: 0;
@ -146,3 +148,9 @@ pre.content {
max-height: 8em;
overflow-y: auto;
}
.linked-code-decoration {
background: lightblue;
width: 5px !important;
left: 3px;
}

392
static/index.html

@ -1,392 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Compiler Explorer</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="explorer.css" rel="stylesheet">
<script data-main="main" src="ext/requirejs/require.js"></script>
</head>
<body>
<div class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#navbar-collapse" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#" title="Compiler Explorer">Compiler Explorer</a>
</div>
<li class="navbar-collapse collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-left">
<li class="dropdown" id="lang-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-haspopup="true" aria-expanded="false">
<span class="language-name"></span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu dropdown-brand" id="languages-links">
<li class="template"><a href=""></a></li>
</ul>
</li>
<li><a href="#" id="add-editor" title="Click or drag to desired destination">Editor</a></li>
<li><a href="#" id="add-diff" title="Click or drag to desired destination">Diff View</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">More<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#" data-target="#settings" data-toggle="modal">Settings</a></li>
<li class="divider"></li>
<li><a href="#" id="ui-reset">Reset UI layout</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#" id="share">Share</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Help<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="https://www.patreon.com/mattgodbolt"
title="Help Compiler Explorer - become a Patron">
<img height="20" width="20" src="assets/patreon_logo.png">&nbsp; Become a Patron</a></li>
<li class="if-github-enabled">
<a href="https://github.com/mattgodbolt/compiler-explorer">
<img height="20" width="20" src="assets/GitHub-Mark-20px.png">&nbsp;Source</a>
</li>
<li><a href="https://github.com/mattgodbolt/compiler-explorer/wiki">Wiki</a></li>
<li><a href="https://github.com/mattgodbolt/compiler-explorer/issues">Report an issue</a></li>
<li class="divider"></li>
<li class="social if-share-enabled">
<g:plusone annotation="inline" width="150"></g:plusone>
</li>
<li class="social if-share-enabled">
<a href="https://twitter.com/share" class="twitter-share-button"
data-via="mattgodbolt">Tweet about</a></li>
<li class="divider"></li>
<li><a href="#" id="thanks-to">Thanks to...</a></li>
<li><a href="http://xania.org/201609/how-compiler-explorer-runs-on-amazon">How it works</a></li>
<li><a href="mailto:matt@godbolt.org">Contact the author</a></li>
<li><a href="http://xania.org/MattGodbolt" rel="author">About the author</a></li>
</ul>
</li>
</ul>
</li>
</div>
</div>
<div id="root"></div>
<div class="gl_keep template">
<div id="codeEditor">
<div class="top-bar btn-toolbar" role="toolbar">
<div class="btn-group btn-group-sm">
<button title="Decrease font size"
class="btn btn-default btn-sm decrease-font-size">
<span class="glyphicon glyphicon-sm glyphicon-font"></span>
</button>
<button title="Reset font size"
class="btn btn-default btn-sm reset-font-size">
<span class="glyphicon glyphicon-sm glyphicon-font"></span>
</button>
<button title="Increase font size"
class="btn btn-default btn-sm increase-font-size">
<span class="glyphicon glyphicon-sm glyphicon-font"></span>
</button>
</div>
<div class="btn-group btn-group-sm">
<button title="Load or save text" class="btn btn-default btn-sm load-save">
<span class="glyphicon glyphicon-floppy-disk"></span>
</button>
<button title="Add a new compiler for this source (click or drag)"
class="btn btn-default btn-sm add-compiler">
<span class="glyphicon glyphicon-open"></span>
</button>
</div>
</div>
<div class="monaco-placeholder"></div>
</div>
<div id="compiler">
<div class="top-bar">
<table>
<tr>
<td><select class="compiler-picker" placeholder="Select a compiler..."></select></td>
<td><input class="options form-control" type="text" placeholder="compiler options..."
size="256"></td>
</tr>
</table>
<div class="btn-group btn-group-sm filters" data-toggle="buttons">
<button class="btn btn-sm" title="Compile to binary and disassemble the output"
data-bind="binary">
<span>11010</span>
</button>
<button class="btn btn-sm active nonbinary" title="Filter unused labels from the output"
data-bind="labels">
<span>.LX0:</span>
</button>
<button class="btn btn-sm active nonbinary" title="Filter all assembler directives from the output"
data-bind="directives">
<span>.text</span>
</button>
<button class="btn btn-sm active nonbinary"
title="Remove all lines which are only comments from the output"
data-bind="commentOnly">
<span>//</span>
</button>
<button class="btn btn-sm active" title="Output disassembly in Intel syntax"
data-bind="intel">
<span>Intel</span>
</button>
</div>
<div class="btn-group btn-group-sm">
<button title="Decrease font size"
class="btn btn-default btn-sm decrease-font-size">
<span class="glyphicon glyphicon-sm glyphicon-font"></span>
</button>
<button title="Reset font size"
class="btn btn-default btn-sm reset-font-size">
<span class="glyphicon glyphicon-sm glyphicon-font"></span>
</button>
<button title="Increase font size"
class="btn btn-default btn-sm increase-font-size">
<span class="glyphicon glyphicon-sm glyphicon-font"></span>