~lattis/meson-playground

b2cbb8f484b8ecb981a015544f212d72c8f5ae17 — Stone Tickle 1 year, 2 months ago
initial commit
7 files changed, 390 insertions(+), 0 deletions(-)

A .gitignore
A build.sh
A deploy.sh
A emscripten.ini.example
A index.html
A main.js
A style.css
A  => .gitignore +3 -0
@@ 1,3 @@
muon
emscripten.ini
build

A  => build.sh +32 -0
@@ 1,32 @@
#!/bin/sh

set -eu

build="build"
meson="${MESON:-meson}"

if [ ! -d "muon" ]; then
	git clone https://git.sr.ht/~lattis/muon muon
fi

mkdir -p "$build"

if [ ! -d "$build/muon" ]; then
	"$meson" setup \
		--cross ./emscripten.ini \
		-Dsamurai=disabled \
		-Dlibcurl=disabled \
		-Dlibarchive=disabled \
		-Dlibpkgconf=disabled \
		-Ddocs=disabled \
		-Dbestline=disabled \
		-Doptimization=s \
		-Ddebug=false \
		"$build/muon" "muon"
fi

ninja -C "$build/muon"

mkdir -p "$build/playground"

cp index.html main.js style.css "$build/muon/muon.js" "$build/muon/muon.wasm" "$build/playground"

A  => deploy.sh +11 -0
@@ 1,11 @@
#!/bin/sh

set -eu

build="build"
cd "$build/playground"

rm -rf site.tar.gz
tar -cvz index.html main.js style.css muon.js muon.wasm > site.tar.gz

hut pages publish -d lattis.srht.site site.tar.gz

A  => emscripten.ini.example +12 -0
@@ 1,12 @@
[binaries]
c = 'emsdk/upstream/emscripten/emcc'
ar = 'emsdk/upstream/emscripten/emar'

[properties]
root = 'emsdk/upstream/emscripten/system'

[host_machine]
system = 'emscripten'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

A  => index.html +63 -0
@@ 1,63 @@
<!doctype html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>meson playground</title>
    <link href='style.css' rel='stylesheet' type='text/css' />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <div class="wrapper">
      <div class="editor">
        <div class="line-numbers">
            <span></span>
        </div>
        <textarea id="input" spellcheck="false" rows="8"></textarea>
      </div>

      <div>
        <textarea id="output" rows="5" readonly></textarea>
      </div>

      <div class="bottom">
        <div class="lhs">
          <div>
            <button onclick="muonRun(['analyze', '-l', '-O', 'meson.build'])">lint</button>
          </div>

          <hr class="vr" />

          <div>
            <input type=checkbox checked id="analyze_while_typing">
              auto
            </input>
          </div>
        </div>

        <div class="rhs">
          <div>
            <a href="#" id="share">share</a>
          </div>
        </div>

      </div>

      <div class="footer">
        <small>
          powered by <a href="https://muon.build">muon</a>
        </small>

        <div>
          <hr class="vr" />
        </div>

        <small>
          <a href="https://git.sr.ht/~lattis/meson-playground">source</a>
        </small>
      </div>
    </div>

    <script src="main.js"></script>
  </body>
</html>

A  => main.js +146 -0
@@ 1,146 @@
/*
 * require function from
 * https://stackoverflow.com/questions/6971583/node-style-require-for-in-browser-javascript/19625245#19625245
 * ----------------
 */

function require(url){
  if (url.toLowerCase().substr(-3)!=='.js') url+='.js'; // to allow loading without js suffix;
  if (!require.cache) require.cache=[]; //init cache
  var exports=require.cache[url]; //get from cache
  if (!exports) { //not cached
    try {
      exports={};
      var X=new XMLHttpRequest();
      X.open("GET", url, 0); // sync
      X.send();
      if (X.status && X.status !== 200)  throw new Error(X.statusText);
      var source = X.responseText;
      // fix (if saved form for Chrome Dev Tools)
      if (source.substr(0,10)==="(function("){
        var moduleStart = source.indexOf('{');
        var moduleEnd = source.lastIndexOf('})');
        var CDTcomment = source.indexOf('//@ ');
        if (CDTcomment>-1 && CDTcomment<moduleStart+6) moduleStart = source.indexOf('\n',CDTcomment);
        source = source.slice(moduleStart+1,moduleEnd-1);
      }
      // fix, add comment to show source on Chrome Dev Tools
      source="//@ sourceURL="+window.location.origin+url+"\n" + source;
      //------
      var module = { id: url, uri: url, exports:exports }; //according to node.js modules
      var anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module
      anonFn(require, exports, module); // call the Fn, Execute the module
      require.cache[url]  = exports = module.exports; //cache obj exported by module
    } catch (err) {
      throw new Error("Error loading module "+url+": "+err);
    }
  }
  return exports; //require returns object exported by module
}

/*
 * utils
 */

function stripEscapes(str) {
  var r = '';
  var esc = false;

  for (var i = 0; i < str.length; i++) {
    if (esc) {
      if (str.charAt(i) == 'm') {
            esc = false;
      }
    } else if (str.charCodeAt(i) == 033) {
      esc = true;
    } else {
      r += str.charAt(i);
    }
  }

  return r;
}

/*
 * emscripten stuff
 * ----------------
 */
var factory = require('./muon.js');

function muonRun(arguments) {
  factory({
    arguments: arguments,
    printErr: (function() {
      var element = document.getElementById('output');
      if (element) element.value = ''; // clear browser cache
      return function(text) {
        text = stripEscapes(text);

        if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
        if (element) {
          element.value += text + "\n";
          element.scrollTop = element.scrollHeight; // focus on bottom
        }
      };
    })(),
    stdinPos: 0,
    stdin: (function() {
      var element = document.getElementById('input');
      var str = element.value;
      var idx = 0;

      return function() {
        if (idx >= str.length) {
          return null;
        }

        var c = str.charCodeAt(idx);
        idx += 1;
        return c;
      }
    })(),
  }).then((instance) => {
    var element = document.getElementById('output');
    if (element.value == '') {
      element.value = 'no errors';
    }
  });
}

const textarea = document.querySelector('textarea')
const lineNumbers = document.querySelector('.line-numbers')

function textChanged(elem) {
  const numberOfLines = elem.value.split('\n').length

  lineNumbers.innerHTML = Array(numberOfLines)
    .fill('<span></span>')
    .join('')

  const data = btoa(elem.value);
  const uri = window.location.origin + window.location.pathname + '?t=' + data
  document.getElementById('share').href = uri;

  if (document.getElementById('analyze_while_typing').checked) {
    muonRun(['analyze', '-l', '-O', 'meson.build']);
  }
}

textarea.addEventListener('input', event => { textChanged(event.target); });

(function() {
  const params = new Proxy(new URLSearchParams(window.location.search), {
    get: (searchParams, prop) => searchParams.get(prop),
  });
  if (params.t == null) {
    var element = document.getElementById('input');
    element.value = "project('example', 'c')\n\nexecutable('exe', 'main.c')\n";
  } else {
    let str = atob(params.t);

    var element = document.getElementById('input');
    element.value = str;
  }

  textChanged(element);
})();

A  => style.css +123 -0
@@ 1,123 @@
body {
  display: flex;
  justify-content: center;
  margin: none;
  padding: 0;
  padding-top: 0;
  font-family: sans-serif;
  background-color: #282A36;
  color: #F8F8F2;
}

a {
  color: #8BE9FD;
}

small {
  color: #7b7b76;
}

.editor {
  display: inline-flex;
  gap: 10px;
  font-family: monospace;
  line-height: 21px;
  background: #282A36;
  border-radius: 5px 5px 0px 0px;
  border: 1px solid #6272A4;
  /* padding: 20px 10px; */
  width: 100%;
}

textarea {
  line-height: 21px;
  padding: 0;
  border: 0;
  background: #282A36;
  color: #F8F8F2;
  width: 100%;
  outline: none;
  resize: vertical;
}

.line-numbers {
  width: 20px;
  text-align: right;
}

.line-numbers span {
  counter-increment: linenumber;
}

.line-numbers span::before {
  content: counter(linenumber);
  display: block;
  color: #6272A4;
}

#output {
  border-radius: 0px 0px 5px 5px;
  border: 1px solid #6272A4;
}

button {
  border-radius: 0.1875rem;
  box-shadow: 0 0.125rem 5px rgba(0, 0, 0, 0.2);
  box-sizing: border-box;
  color: #BD93F9;
  background-color: #21222C;
  cursor: pointer;
  display: inline-flex;
  height: 1.75rem;
  padding: 0 0.625rem;
  justify-content: center;
  min-width: 4.063rem;
  text-decoration: none;
  align-items: center;
  border: 0.0625rem solid #6272A4;
}

.bottom {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}

.footer {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

.lhs {
  display: flex;
  flex-direction: row;
  align-items: center;
}

.rhs {
  display: flex;
  flex-direction: row;
  align-items: center;
}

.wrapper {
  padding: 10px;
  width: 80%;
  max-width: 800px;
}

hr.vr {
  width: 0;
  height: 9px;
  margin-left: 5px;
  margin-right: 5px;
}

@media screen and (max-width: 500px) {
  .wrapper {
    width: 100%;
  }
}