Last active
April 20, 2024 06:28
-
-
Save Alhadis/7ecf06decc7876c8b35007124654a13e to your computer and use it in GitHub Desktop.
More junk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const {TextBuffer, TextEditor, Range, Point} = require("atom"); | |
const ScreenLineBuilder = loadFromCore("text-buffer/lib/screen-line-builder"); | |
const {emitText} = ScreenLineBuilder.prototype; | |
const exoticNewlines = "\r"; | |
ScreenLineBuilder.prototype.emitText = function(...args){ | |
const {buffer} = this.displayLayer; | |
const [char] = args; | |
if(char === buffer.getPreferredLineEnding() && exoticNewlines.includes(char)){ | |
this.currentBuiltInClassNameFlags |= 1 << 6; // LINE_ENDING | |
return this.emitLineEnding(); | |
} | |
return emitText.call(this, ...args); | |
}; | |
const {lineLengthForRow} = TextBuffer.prototype; | |
TextBuffer.prototype.lineLengthForRow = function(...args){ | |
const eol = this.getPreferredLineEnding(); | |
if(exoticNewlines.includes(eol)){ | |
const [line] = args; | |
const breaks = this.findAllSync(eol); | |
const start = Point.fromObject(breaks[line + 0].end); | |
const end = Point.fromObject(breaks[line + 1].start); | |
return this.characterIndexForPosition(end) - this.characterIndexForPosition(start); | |
} | |
return lineLengthForRow.call(this, ...args); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env fontforge | |
New() | |
# NOTE: The weird-looking song-and-dance is because FontForge's | |
# scripting language lacks support for user-defined functions… | |
chars = ["a", "b", "c", "d"] | |
case = "lower" | |
i = 0 | |
while(i++ < 2) | |
j = 0 | |
while(j < SizeOf(chars)) | |
code = 0x61 | |
if("upper" == case) | |
code = 0x41 | |
endif | |
Select(code + j) | |
Import("glyphs/" + case + "-" + chars[j++] + ".svg", 0, 64) | |
endloop | |
case = "upper" | |
endloop | |
SelectAll() | |
DontAutoHint() | |
# Choose an ugly-looking name unlikely to be used | |
name = "canvas-html-font-test" | |
SetTTFName(0x409, 0, "-") | |
SetTTFName(0x409, 1, name) | |
SetTTFName(0x409, 3, name) | |
SetTTFName(0x409, 4, name) | |
SetTTFName(0x409, 6, name) | |
SetTTFName(0x409, 5, "Version 1") | |
SetTTFName(0x409, 2, "Regular") | |
Generate(name + ".ttf") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/^--- /,/^+++ /d | |
/^\\ No newline at end of file$/d | |
s/^-/[31m-/ | |
s/^+/[32m+/ | |
s/^@/[36m@/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/^--- /,/^+++ /d | |
/^\\ No newline at end of file$/d |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require "pp" | |
module Linguist | |
module Grammars | |
# Public: Recognised hosting providers for Git repositories. | |
# | |
# Hosted repositories are required to have URLs of the form: | |
# https://[www.]${HOST}/${USERNAME}/${REPOSITORY_NAME}[.git] | |
HOSTS = %w[ | |
github.com | |
bitbucket.org | |
gitlab.com | |
].freeze | |
# Internal: Path to Linguist's root directory. | |
ROOT = File.expand_path("../../..", __FILE__) | |
# Public: Get the path to the directory containing the language grammar JSON files. | |
# | |
# Returns a String. | |
def self.path | |
File.expand_path("grammars", ROOT) | |
end | |
# Public: Expand a whitelisted hostname into an HTTPS URL. | |
# | |
# If the string begins with a protocol component and/or | |
# redundant subdomain, they're stripped before lookup. | |
# | |
# Example | |
# | |
# Grammars.expand_hostname "https://www.github.com" | |
# Grammars.expand_hostname "www.github.com" | |
# Grammars.expand_hostname "github.com" | |
# Grammars.expand_hostname "GitHub" | |
# # => "https://github.com/" | |
# | |
# Grammars.expand_hostname "https://github.com/", fqdn: true | |
# # => "github.com" | |
# | |
# name - Hostname, with or without TLD | |
# fqdn - Return a fully-qualified domain name, rather than an HTTPS URL. | |
# | |
# Returns a String, or nil if input wasn't a supported hostname. | |
def self.expand_hostname(host, fqdn = false) | |
host = host.downcase.gsub(%r{^[+a-z]*:(?://)?(?:www\.)?|/$}, "") | |
host = HOSTS.select {|str| str.start_with? host}.first | |
host = "https://#{host}/" unless fqdn or host.nil | |
host | |
end | |
# Isolate the vendor-name component of a submodule path. | |
# | |
# Example | |
# | |
# Grammars.resolve_path "./vendor/grammars/language-etc/" | |
# Grammars.resolve_path "grammars/language-etc" | |
# Grammars.resolve_path "language-etc" | |
# # => "vendor/grammars/language-etc" | |
# | |
# name - A submodule's name or project-relative path. | |
# absolute - Resolve the absolute, canonicalised path of a submodule directory. | |
# By default, paths are resolved relative to Linguist's root. | |
# | |
# Returns a String. | |
def self.resolve_path(name, absolute: false, ignore_missing: false) | |
name =~ %r{^(?:.*(?:vendor/)?grammars/)?([^/]+)/?$}i | |
path = "vendor/grammars/#{$1}" | |
unless ignore_missing or File.exist?("#{ROOT}/" + path) | |
raise "Submodule '#{path}' does not exist" | |
end | |
path | |
end | |
# Break a repository URL into its separate components. | |
# | |
# Examples | |
# | |
# Grammars.resolve_url "https://github.com/Alhadis/language-etc" | |
# Grammars.resolve_url "git@github.com:Alhadis/language-etc.git" | |
# Grammars.resolve_url "github.com/Alhadis/language-etc" | |
# Grammars.resolve_url "github:Alhadis/language-etc" | |
# Grammars.resolve_url "Alhadis/language-etc" | |
# # => {host: "github.com", user: "Alhadis", repo: "language-etc"} | |
# | |
# Grammars.resolve_url "https://bitbucket.org/bitlang/sublime_cobol" | |
# Gramamrs.resolve_url "bitbucket:bitlang/sublime_cobol" | |
# # => {host: "bitbucket.org", user: "bitlang", repo: "sublime_cobol"} | |
# | |
# input - A URL referring to a whitelisted host: GitHub | |
# strict - Raise an exception for an unrecognised URL | |
# | |
# Returns a Hash with the following fields: | |
# :host - A hostname whitelisted by ::HOSTS array (lowercased) | |
# :user - Username, case-sensitive and `:repo` | |
# :repo - Repository name, sans trailing `.git` suffix | |
# :branch - A specific branch name, if one was specified | |
# :commit - A commit ID (SHA-1 or SHA-256) | |
# :tag - A tag name, typically a version string | |
# :url - Original input passed through ::expand_hostname | |
def self.resolve_url(input, strict: false) | |
hosts = Regexp.union(HOSTS) | |
# Initialise fields | |
url = input | |
host = nil | |
user = nil | |
repo = nil | |
branch = nil | |
commit = nil | |
tag = nil | |
# HTTPS/HTTP link pointing to a recognised host | |
if input =~ %r{ | |
^ | |
(?: | |
# URI scheme | |
(?<scheme> (?:git\+)?https?|git){0} | |
(?<protocol> | |
\g<scheme> : (?!//) | # Tolerate "https:github.com/user/repo" | |
\g<scheme>? : // | # Tolerate "://github.com/user/repo" | |
) | |
# Credentials (ignored since grammar repos must be public) | |
(?<userinfo> | |
(?<username> [^\S:@/]+) | |
(?<password> : [^\S:@/]+)? @ | |
)? | |
)? | |
# Hostname | |
(?:www\.)? # Redundant subdomain | |
(?<host> #{hosts}) # Whitelisted hostnames | |
# Path components | |
/ (?<user> [^/\#@]+) # Username | |
/ (?<repo> [^/\#@]+) # Repository name | |
/? (?<extra> (?!/)\S+)? # Superfluous elements | |
$ | |
}ix | |
host = $~[:host] | |
user = $~[:user] | |
repo = $~[:repo] | |
# As an extension, allow specific branches and revisions to be specified | |
if $~.named_captures.has_key?(:extra) and $~[:extra] =~ %r{ | |
^ | |
(?<rev-type> | |
\# (?<param-sep> :){0} (?!:) | # NPM style syntax: user/repo#rev | |
@ (?<param-sep> /){0} (?!/) # Git syntax: user/repo@rev | |
) | |
(?<rev-value> | |
\g<rev-explicitly-typed> | | |
\g<rev-implicitly-typed> | |
) | |
$ | |
# Public subroutines | |
(?<branch> [^\s/]+(?:/[^\s/]+)++ ){0} | |
(?<commit> \b\h+{4,} ){0} | |
(?<tag> (?!\S*?\h{40,}$) \S+ ){0} | |
# Internal subroutines | |
(?<sep> \b){0} | |
(?<affix> [-_.]?(?:id|name)? \k'sep'){0} | |
(?<rev-implicitly-typed> \g<tag> | \g<commit> | \g<branch>){0} | |
(?<rev-explicitly-typed> | |
(?<param-name> branch|tree) \g<affix> \g<branch> | | |
(?<param-name> rev(?:ision)?|commit) \g<affix> \g<commit> | | |
(?<param-name> tag|release|version|semver) \g<affix> \g<tag> | |
){0} | |
}xi | |
branch = $~[:branch] | |
commit = $~[:commit] | |
tag = $~[:tag] | |
end | |
repo.sub!(/\.git$/, "") | |
# git@provider:user/repo.git | |
elsif input =~ /^git@(#{hosts}):([^\/]+)\/([^\/]+)\.git$/i | |
host = $1 | |
user = $2 | |
repo = $3 | |
# provider:user/repo | |
elsif input =~ /^(github|bitbucket|gitlab):\/?([^\/]+)\/([^\/]+)\/?$/i | |
host = $1 | |
user = $2 | |
repo = $3 | |
# user/repo - Common GitHub shorthand | |
elsif input =~ /^\/?([^\/]+)\/([^\/]+)\/?$/ | |
host = self.expand_hostname "github", fqdn: true | |
url = "https://#{host}/#{user}/#{repo}" | |
user = $1 | |
repo = $2 | |
# Nothing we can reliably identify | |
else | |
raise "Unsupported URL: #{input}" if strict | |
return nil | |
end | |
# Clean-up and sanity checks | |
host = self.expand_hostname(host, true) | |
if !commit.nil? then branch = tag = nil | |
elsif !branch.nil? then tag = nil | |
end | |
return ({ | |
url: url, | |
host: host, | |
user: user, | |
repo: repo, | |
branch: branch, | |
commit: commit, | |
tag: tag, | |
}).freeze | |
end | |
end | |
end | |
if __FILE__ == $0 | |
ARGV.each do |arg| | |
info = Linguist::Grammars.resolve_url(arg) | |
p info | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
class Irrational extends Number{ | |
#ƒ; | |
constructor(ƒ = () => Infinity){ | |
super(ƒ()); | |
this.#ƒ = ƒ; | |
} | |
valueOf(){ | |
return this.#ƒ(); | |
} | |
} | |
// Golden ratio | |
const ϕ = new Irrational(() => (1 + Math.sqrt(5)) / 2); | |
console.log(ϕ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Terminate with an error message | |
# - Usage: die [reason] [exit-code=1] | |
die(){ | |
printf '%s\n' "$1" | |
[ "$2" ] && exit $2 || exit 1 | |
} | |
# Verify that an executable is installed and reachable from $PATH | |
need(){ | |
while [ $# -gt 0 ]; do | |
command -v "$1" 2>&1 >/dev/null || die "Unable to resolve path to $1" 2 | |
shift | |
done | |
} | |
# Read the entirety of standard input and place it $1 | |
# - Usage: slurp [var-name] < [input] | |
slurp(){ | |
while IFS= read -r line; do | |
set -- "$1" "$2`printf '\n%s' "$line"`" | |
done | |
set -- "$1" "`printf '%s\n' "$2" | sed -n 's/^[[:blank:]]//; /[^ \t]/,$p;'`" | |
eval $1=\$2 | |
} | |
export ELECTRON_RUN_AS_NODE=1 | |
export ELECTRON_ENABLE_LOGGING=true | |
export NODE_OPTIONS='--experimental-modules --no-warnings --input-type=module' | |
# Resolve directory for temporary file-storage | |
[ -d "$EAL_TEMP_DIR" ] \ | |
|| export EAL_TEMP_DIR=`mktemp -d /tmp/Alhadis.EAL.XXXXXX` \ | |
|| die 'Failed to create temporary directory. Aborting.' 2 | |
# Resolve paths to different JavaScript interpreters/environments | |
need node deno qjs d8 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en-AU"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> | |
<meta name="viewport" content="initial-scale=1, minimum-scale=1"/> | |
<title>Detached stylesheet loading without Shadow DOM</title> | |
</head> | |
<body><script> | |
"use strict"; | |
async function loadCSSFile(path){ | |
path = String(path).replace(/\/$/, ""); | |
if(!path.startsWith("/")) | |
throw new TypeError("Stylesheet path must be absolute"); | |
const cut = path.lastIndexOf("/"); | |
const dir = path.slice(0, cut); | |
const name = path.slice(cut + 1); | |
const url = new URL("file://"); | |
url.pathname = dir; | |
let init = Promise.withResolvers(); | |
const frame = Object.assign(document.createElement("iframe"), { | |
src: "about:blank", | |
width: 100, | |
height: 100, | |
sandbox: "allow-same-origin", | |
onerror: init.reject, | |
onload: init.resolve, | |
}); | |
document.currentScript.parentElement.insertBefore(frame, document.currentScript); | |
await init.promise; | |
init = Promise.withResolvers(); | |
const doc = frame.contentDocument; | |
const base = Object.assign(doc.createElement("base"), {href: url}); | |
const link = Object.assign(doc.createElement("link"), { | |
onerror: init.reject, | |
onload: init.resolve, | |
type: "text/css", | |
rel: "stylesheet", | |
href: name, | |
}); | |
doc.head.append(base, link); | |
await init.promise; | |
const {sheet} = link; | |
frame.remove(); | |
link.href = ""; | |
return sheet; | |
} | |
loadCSSFile("/Users/Alhadis/Labs/Sandbox/rel.css").then(x => { | |
console.log(x); | |
}); | |
</script></body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Merge adjacent strings in an array. | |
* @param {*} input - An array to operate on; non-arrays (except for symbols) are stringified | |
* @param {Boolean} [unwrap=false] - Unbox single-element arrays after normalising | |
* @param {Boolean} [recurse=true] - Merge adjacent strings in subarrays | |
* @param {WeakSet} [refs] - List of normalised objects used internally to avoid infinite loops | |
* @return {Array|String} | |
*/ | |
function normalise(input, unwrap = false, recurse = true, refs = new WeakSet()){ | |
if(!isObj(input)) | |
return "symbol" === typeof input ? input : String(input); | |
if(refs.has(input)) return; | |
refs.add(input); | |
// Merge adjacent strings together | |
for(let i = input.length - 1, j = 0; i >= -1; --i){ | |
if("string" !== typeof input[i]){ | |
if(j > 1){ | |
const chars = input.splice(i + 1, j, ""); | |
input[i + 1] = chars.join(""); | |
} | |
j = 0; | |
// Recurse | |
if(recurse && Array.isArray(input[i])) | |
input[i] = normalise(input[i], unwrap, true, refs); | |
} | |
else ++j; | |
} | |
if(unwrap) | |
while(Array.isArray(input) && 1 === input.length) | |
input = input[0]; | |
return input; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Parse the porcelain output of git-status(1). | |
* | |
* @example parseGitStatus(execSync("git status --ignored --porcelain=2 -bz")); | |
* @param {String} input | |
* @return {Object} | |
*/ | |
export function parseGitStatus(input){ | |
input = String(input).trim(); | |
const raw = ~input.indexOf("\0"); | |
const status = {}; | |
const changes = []; | |
const conflicts = []; | |
const ignored = []; | |
const untracked = []; | |
const statusCodes = { | |
".": "unmodified", | |
"?": "untracked", | |
"!": "ignored", | |
M: "modified", | |
A: "added", | |
D: "deleted", | |
R: "renamed", | |
C: "copied", | |
T: "typechange", | |
X: "unknown", | |
U: "updated but unmerged", | |
}; | |
const oct = x => x.map(n => parseInt(n, 8)); | |
const sub = ([S, SC, SM, SU]) => "S" === S && { | |
commitChanged: "C" === SC, | |
hasTrackedChanges: "M" === SM, | |
hasUntrackedChanges: "U" === SU, | |
}; | |
for(const line of input.split(raw ? "\0" : "\n").filter(Boolean)){ | |
const [prefix, ...entries] = line.split(" "); | |
switch(prefix){ | |
// Branch header | |
case "#": { | |
switch(entries.shift()){ | |
case "branch.oid": | |
status.currentCommit = "(initial)" === entries[0] ? null : entries[0]; | |
break; | |
case "branch.head": | |
status.currentBranch = "(detached)" === entries[0] ? null : entries[0]; | |
break; | |
case "branch.upstream": | |
(status.upstream = status.upstream || {}).branch = entries[0]; | |
break; | |
case "branch.ab": | |
status.upstream = status.upstream || {}; | |
status.upstream.ahead = Math.abs(parseInt(entries[0])); | |
status.upstream.behind = Math.abs(parseInt(entries[1])); | |
break; | |
} | |
break; | |
} | |
// Changed file | |
case "1": | |
case "2": { | |
1 === +prefix && entries.splice(7, 0, null); | |
let [XY, s, mH, mI, mW, hH, hI, score, path] = entries; | |
[mH, mI, mW] = oct([mH, mI, mW]); | |
path = path.split(raw ? "\0" : "\t"); | |
changes.push({ | |
state: {index: statusCodes[XY[0]], workTree: statusCodes[XY[1]]}, | |
submodule: sub(s), | |
mode: mH === mI && mI === mW ? mH : {head: mH, index: mI, workTree: mW}, | |
hash: hH === hI ? hH : {head: hH, index: hI}, | |
similarity: score && {action: {R: "renamed", C: "copied"}[score[0]], amount: +score.substring(1)}, | |
path: path[1] ? {old: path[1], new: path[0]} : path[0], | |
}); | |
break; | |
} | |
// Unmerged path (conflict) | |
case "u": { | |
let [XY, s, m1, m2, m3, mW, h1, h2, h3, path] = entries; | |
[m1, m2, m3, mW] = oct([m1, m2, m3, mW]); | |
conflicts.push({ | |
state: {us: statusCodes[XY[0]], them: statusCodes[XY[1]]}, | |
submodule: sub(s), | |
mode: {stage1: m1, stage2: m2, stage3: m3, workTree: mW}, | |
hash: {stage1: h1, stage2: h2, stage3: h3}, | |
path, | |
}); | |
break; | |
} | |
// Untracked or ignored | |
case "?": untracked.push(entries.join(" ")); break; | |
case "!": ignored .push(entries.join(" ")); break; | |
} | |
} | |
status.isNewRepo = null === status.currentCommit; | |
status.isDetached = null === status.currentBranch; | |
status.isClean = !(changes.length + conflicts.length + ignored.length + untracked.length); | |
if(changes.length) status.changes = changes; | |
if(conflicts.length) status.conflicts = conflicts; | |
if(ignored.length) status.ignored = ignored; | |
if(untracked.length) status.untracked = untracked; | |
return status; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {isPrimitive, uint} from "./utils/misc.mjs"; | |
export default class Point extends Uint32Array { | |
static [Symbol.species] = Array; | |
static get zero(){ | |
return new this(0, 0); | |
} | |
static get zenith(){ | |
const max = (this.BYTES_PER_ELEMENT ** 16) - 1; | |
return new this(max, max); | |
} | |
static parse(input){ | |
if(isPrimitive(input)) | |
return [input, 0]; | |
else if("function" === typeof input[Symbol.iterator]){ | |
const [line, column] = input[Symbol.iterator](); | |
return [line, column]; | |
} | |
else{ | |
const keys = Object.keys(input); | |
const line = keys.find(key => /^(?:line|row|x|0)$/i); | |
const column = keys.find(key => /^(?:column|col|y|1)$/i); | |
if(line && column) | |
return [input[line], input[column]]; | |
} | |
throw new TypeError(`Invalid vector: ${input}`); | |
} | |
constructor(...args){ | |
super(2); | |
if(1 === args.length){ | |
args = Point.parseVector(args); | |
this[0] = uint(args[0]); | |
this[1] = uint(args[1]); | |
} | |
else{ | |
this[0] = uint(args[0]); | |
this[1] = uint(args[1]); | |
} | |
} | |
get row(){ return this.line; } | |
set row(to){ this.line = to; } | |
get line(){ return this[0]; } | |
set line(to){ | |
if(!Number.isNaN(to = uint(to))) | |
this[0] = to; | |
} | |
get column(){ | |
return uint(this[1]); | |
} | |
set column(to){ | |
if(Number.isNaN(to = uint(to))) | |
this[1] = to; | |
} | |
valueOf(){ | |
return parseFloat(`${this.line}.${this.column}`); | |
} | |
toJSON(){ | |
return [this.line, this.column]; | |
} | |
toString(){ | |
return `[${this.line}, ${this.column}]`; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# vim: ts=4 | |
def define_effect(name, start_esc, end_esc) | |
start_esc = "\e[#{start_esc.join(";")}m" if start_esc .is_a? Array | |
end_esc = "\e[#{end_esc.join(";")}m" if end_esc .is_a? Array | |
start_esc = "\e[#{start_esc}m" if start_esc .is_a? Integer | |
end_esc = "\e[#{end_esc}m" if end_esc .is_a? Integer | |
self.class.define_method(name.to_sym) do |*args| | |
arg = args.first | |
case arg | |
when true, nil then start_esc | |
when false then end_esc | |
when arg.is_a?(String) then start_esc + arg + end_esc | |
end | |
end | |
end | |
# Public: Helpers functions for preparing highlighted terminal output. | |
module SGR | |
# Public: Determine if colours should be used when writing output to an IO stream. | |
# | |
# Examples | |
# | |
# $ ruby sgr-test.rb 2>/dev/null | |
# SGR.use_colours?(STDOUT) # => true | |
# SGR.use_colours?(STDERR) # => false | |
# | |
# io - An IO stream, STDOUT by default | |
# | |
# Returns a Boolean. | |
def self.use_colours?(io = $>) | |
return true if forced? | |
return false if disabled? or dumb? | |
return io.isatty | |
end | |
define_effect :bold, 1, 21 | |
define_effect :fade, 2, 22 | |
define_effect :italic, 3, 23 | |
define_effect :ul, 7, 27 | |
# Simple colour definitions | |
def self.red (*args); get_colour 1, *args; end | |
def self.green (*args); get_colour 2, *args; end | |
def self.yellow (*args); get_colour 3, *args; end | |
def self.blue (*args); get_colour 4, *args; end | |
def self.pink (*args); get_colour 5, *args; end | |
def self.cyan (*args); get_colour 6, *args; end | |
def self.grey (*args); get_colour 7, *args; end | |
def self.dark_red (*args); get_colour [1, 88, "80;0;0"], *args; end | |
def self.dark_green (*args); get_colour [2, 22, "0;80;0"], *args; end | |
def self.dark_yellow (*args); get_colour [3, 58, "80;80;0"], *args; end | |
def self.dark_blue (*args); get_colour [4, 55, "0;0;80"], *args; end | |
def self.dark_pink (*args); get_colour [4, 89, "80;0;80"], *args; end | |
def self.dark_cyan (*args); get_colour [6, 23, "0;80;80"], *args; end | |
def self.dark_grey (*args); get_colour [7, 234, "80;80;80"], *args; end | |
# Internal: Resolve arguments for a method that returns an ANSI escape. | |
# | |
# start_esc - Sequence that begins the desired effect | |
# end_esc - Sequence that ends the desired effect | |
# enable - Which of the two sequences to use | |
# io - IO stream, STDOUT by default | |
# | |
# Returns a String if coloured output is enabled for io, nil otherwise. | |
def self.get_ansi(start_esc, end_esc, enable = true, io = $>) | |
enable ? start_esc : end_esc if use_colours?(io) | |
end | |
# Internal: Resolve arguments for a method that returns an ANSI colour sequence. | |
# | |
# values - An array of integers or strings corresponding to the colour values | |
# best supported by the current terminal emulator: | |
# | |
# values[0]: 8/16 colours | |
# values[1]: 256 colours | |
# values[2]: 16 million colours | |
# | |
# A single colour-spec may be passed as a shorthand for setting all | |
# three colour resolutions. An array with fewer than three entries is | |
# padded out using the last (best) colour value. | |
# | |
# bg - Return an escape for setting background colour, not foreground (text) colour. | |
# | |
# Examples | |
# | |
# # Single colour | |
# SGR.get_colour 6 # => "\e[36m" | |
# SGR.get_colour 51 # => "\e[38;5;51m" | |
# SGR.get_colour "0;92;197" # => "\e[38;2;0;92;197m" | |
# SGR.get_colour 6, true # => "\e[46m" | |
# SGR.get_colour 51, true # => "\e[48;5;51m" | |
# | |
# # Resetting colours | |
# SGR.get_colour nil # => "\e[39m" | |
# | |
# # Adaptive colours (varies based on colour support) | |
# SGR.get_colour [6, 51, "0;92;197"] | |
# | |
# Returns a String if coloured output is enabled for io, nil otherwise. | |
def self.get_colour(values, bg = false) | |
end | |
# Retrieve the value of an environment variable. | |
# | |
# aliases - List of variable names that are different | |
# ways to specify the same thing. | |
# | |
# Returns a String containing the value of the first alias | |
# that matches an environment variable. If nothing matched, | |
# nil is returned instead. | |
def self.get_var(*aliases) | |
aliases.each do |name| | |
return ENV[name] if ENV.include?(name) | |
end | |
nil | |
end | |
# Return true if colours have been disabled in one's environment. | |
# | |
# Behaviour should conform to that of http://no-color.org/. | |
def self.disabled? | |
!get_var("NO_COLOR", "NO_COLOUR").nil? | |
end | |
# Return true if running inside a TTY without ANSI escape support. | |
def self.dumb? | |
term = get_var("TERM") | |
if term == "dumb" or term == "unknown" | |
true | |
elsif (term.nil? or term == "") and not using_windows? | |
true | |
else | |
false | |
end | |
end | |
# Return true if ANSI escape sequences should be output indiscriminately, | |
# possibly at a specific colour-depth. | |
# | |
# This method has greater priority than `disabled?`, and its logic should | |
# stay consistent with nodejs/node#37477 (upon which this logic is based). | |
# | |
# Returns a Boolean. | |
def self.forced? | |
case get_var("FORCE_COLOR", "FORCE_COLOUR") | |
when "", "1", "2", "3", "true", 1, 2, 3, true | |
true | |
else | |
false | |
end | |
end | |
# Return true if user needs a better operating system. | |
def self.using_windows? | |
if RUBY_PLATFORM =~ /bccwin|cygwin|emx|mingw|mswin|wince/i | |
true | |
elsif get_var("SystemRoot", "SYSTEMROOT") and get_var("ComSpec", "COMSPEC") | |
true | |
else | |
false | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- -*- svg -*- --> | |
<!DOCTYPE html> | |
<html lang="en-AU"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="initial-scale=1, minimum-scale=1" /> | |
<title></title> | |
<style> | |
img{ | |
filter: url('data:image/svg+xml,\ | |
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">\ | |
<filter id="tint">\ | |
<feColorMatrix in="SourceGraphic" type="matrix" values="\ | |
0 0 0 0 0 \ | |
0 1 0 0 0 \ | |
0 0 0 0 0 \ | |
0 0 0 1 0 \ | |
"/>\ | |
</filter>\ | |
</svg>\ | |
#tint'); | |
} | |
</style> | |
</head> | |
<body> | |
<img src="alpha.png"/> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {readFile} from "./utils/env.mjs"; | |
/** | |
* A variable-length buffer of plain-text data. | |
* @public | |
* @class | |
*/ | |
export default class TextBuffer extends Array { | |
static [Symbol.species] = Array; | |
/** | |
* Load the contents of a file from disk. | |
* | |
* @param {String} path - Pathname of file to load | |
* @param {String} [encoding="utf8"] - Character encoding of input | |
* @return {TextBuffer} | |
* @public | |
*/ | |
static load(path, encoding = "utf8"){ | |
return new this(readFileSync(path, encoding)); | |
} | |
/** | |
* Initialise a new buffer, optionally propagating it with data. | |
* | |
* @param {String} [data=""] | |
* @param {Number} [startIndex=0] | |
* @constructor | |
* @public | |
*/ | |
constructor(data = "", startIndex = 0){ | |
super(...data.split(/(?<=\r?\n|\r(?!\n))(?=^)/m) | |
.map((s, i) => new Line(s, i + startIndex))); | |
this.#startIndex = startIndex; | |
} | |
/** | |
* @member {Number} startIndex | |
* @summary Value to start numbering lines from. | |
* @desc Setting this property will update the index of every | |
* {@link Line} currently held by the buffer object. | |
*/ | |
#startIndex = 0; | |
get startIndex(){ return this.#startIndex; } | |
set startIndex(to){ | |
if(Number.isNaN(to = Math.max(+to, 0)) || to === this.#startIndex) return; | |
this.#startIndex = to; | |
this.forEach((line, index) => line.index = index + to); | |
} | |
/** | |
* Concatenate each line and return the result. | |
* @return {String} | |
*/ | |
join(){ | |
return super.join(""); | |
} | |
/** | |
* Convert the buffer to its string representation using {@link TextBuffer#format}. | |
* @param {FormatOptions} [opts={}] | |
* @return {String} | |
* @public | |
*/ | |
toString(opts = {}){ | |
return this.format(opts); | |
} | |
/** | |
* Extract a range of {@link Char} objects from the buffer. | |
* | |
* Unless both arguments are arrays of the form `[line, column]`, | |
* the method acts similarly to {@link Array.prototype.slice}. | |
* | |
* @example thisFile.slice([1,0], [1,13]).join("") == '"use strict";'; | |
* @param {Number|Number[]} from | |
* @param {Number|Number[]} to | |
* @return {Char[]} | |
*/ | |
slice(from, to){ | |
if(Array.isArray(from) && Array.isArray(to)){ | |
if(to[0] < from[0] || to[0] === from[0] && to[1] < from[1]) | |
[from, to] = [to, from]; | |
// Single-line slice | |
if(from[0] === to[0]) | |
return this[from[0]].slice(from[1], to[1]).flat(9); | |
// Multi-line slice | |
const lines = super.slice(from[0], to[0]); | |
lines[0] = lines[0].slice(from[1]); | |
lines[lines.length - 1] = lines[lines.length - 1].slice(0, to[1]); | |
return lines.flat(9); | |
} | |
return super.slice(from, to); | |
} | |
} | |
/** | |
* A row of {@link Cell|Cells} containing character data. | |
* @public | |
* @class | |
*/ | |
export class Line extends Array { | |
static [Symbol.species] = Array; | |
terminator = ""; | |
#index = 0; | |
constructor(data = "", index = 0){ | |
data = [...data]; | |
let terminator = ""; | |
if("\n" === data[data.length - 1]) terminator = data.pop(); | |
if("\r" === data[data.length - 1]) terminator = data.pop() + terminator; | |
super(...data.map((s, i) => new Cell(s, i))); | |
this.terminator = terminator; | |
this.index = index; | |
} | |
get index(){ | |
return this.#index; | |
} | |
set index(to){ | |
if(Number.isNaN(to = +to)) return; | |
this.#index = Math.max(to, 0); | |
} | |
slice(...args){ | |
const cells = [...this]; | |
if(this.terminator) | |
cells.push(this.terminator); | |
return super.slice.apply(cells, args); | |
} | |
join(){ | |
return super.join("") + this.terminator; | |
} | |
toString(){ | |
return this.join(); | |
} | |
} | |
/** | |
* A single grapheme representing one column of text. | |
* @public | |
* @class | |
*/ | |
export class Cell { | |
before = ""; | |
after = ""; | |
text = ""; | |
#index = 0; | |
constructor(text, index = 0){ | |
this.text = String(text || ""); | |
this.index = index; | |
} | |
get index(){ | |
return this.#index; | |
} | |
set index(to){ | |
if(Number.isNaN(to = +to)) return; | |
this.#index = Math.max(to, 0); | |
} | |
toString(){ | |
return this.before + this.text + this.after; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
atom = string | "*" | |
scope = atom ("." atom)* | |
path = "^"? scope (">"? scope)* "$"? | |
group = "(" selector ")" | |
filter = ("L:"|"R:"|"B:") (group | path) | |
expression = "-"? (filter | group | path) | |
composite = expression ([|&-] expression)* | |
selector = composite ("," composite)* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(?x) | |
(?<atom> \g<string> | \*){0} | |
(?<scope> \g<atom> (\. \g<atom>)*){0} | |
(?<path> \^? \g<scope> (>? \g<scope>)* \$?){0} | |
(?<group> \( \g<selector> \)){0} | |
(?<filter> [LRB]: (\g<group> | \g<path>)){0} | |
(?<expression> -? (\g<filter> | \g<group> | \g<path>)){0} | |
(?<composite> \g<expression> ([-\|&] \g<expression>)*){0} | |
(?<selector> \g<composite> (, \g<composite>)*){0} | |
\g<selector> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en-AU"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="initial-scale=1, minimum-scale=1" /> | |
<title>Crude trigonometry examples</title> | |
<style> | |
body{ | |
background: #ccc; | |
} | |
#pad{ | |
background: #fff 100px 100px url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTAwcHgiIGhlaWdodD0iMTAwcHgiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48c3R5bGU+cGF0aHtvcGFjaXR5Oi4yNTtmaWxsOm5vbmU7c3Ryb2tlOiMwMDA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fWxpbmV7b3BhY2l0eTouMTtmaWxsOm5vbmU7c3Ryb2tlOiMxODE4MTY7c3Ryb2tlLXdpZHRoOi41O3N0cm9rZS1taXRlcmxpbWl0OjEwO308L3N0eWxlPjxwYXRoIGlkPSJFeHRlbnRzIiBjbGFzcz0ic3QwIiBkPSJNOTkuNSwwdjk5LjVIMCIvPjxsaW5lIGlkPSJYMSIgeDE9IjEwIiB5MT0iMCIgeDI9IjEwIiB5Mj0iOTkiLz48bGluZSBpZD0iWDIiIHgxPSIyMCIgeTE9IjAiIHgyPSIyMCIgeTI9Ijk5Ii8+PGxpbmUgaWQ9IlgzIiB4MT0iMzAiIHkxPSIwIiB4Mj0iMzAiIHkyPSI5OSIvPjxsaW5lIGlkPSJYNCIgeDE9IjQwIiB5MT0iMCIgeDI9IjQwIiB5Mj0iOTkiLz48bGluZSBpZD0iWDUiIHgxPSI1MCIgeTE9IjAiIHgyPSI1MCIgeTI9Ijk5Ii8+PGxpbmUgaWQ9Ilg2IiB4MT0iNjAiIHkxPSIwIiB4Mj0iNjAiIHkyPSI5OSIvPjxsaW5lIGlkPSJYNyIgeDE9IjcwIiB5MT0iMCIgeDI9IjcwIiB5Mj0iOTkiLz48bGluZSBpZD0iWDgiIHgxPSI4MCIgeTE9IjAiIHgyPSI4MCIgeTI9Ijk5Ii8+PGxpbmUgaWQ9Ilg5IiB4MT0iOTAiIHkxPSIwIiB4Mj0iOTAiIHkyPSI5OSIvPjxsaW5lIGlkPSJZMSIgeDE9Ijk5IiB5MT0iOS41IiAgeDI9IjAiIHkyPSI5LjUiLz48bGluZSBpZD0iWTIiIHgxPSI5OSIgeTE9IjE5LjUiIHgyPSIwIiB5Mj0iMTkuNSIvPjxsaW5lIGlkPSJZMyIgeDE9Ijk5IiB5MT0iMjkuNSIgeDI9IjAiIHkyPSIyOS41Ii8+PGxpbmUgaWQ9Ilk0IiB4MT0iOTkiIHkxPSIzOS41IiB4Mj0iMCIgeTI9IjM5LjUiLz48bGluZSBpZD0iWTUiIHgxPSI5OSIgeTE9IjQ5LjUiIHgyPSIwIiB5Mj0iNDkuNSIvPjxsaW5lIGlkPSJZNiIgeDE9Ijk5IiB5MT0iNTkuNSIgeDI9IjAiIHkyPSI1OS41Ii8+PGxpbmUgaWQ9Ilk3IiB4MT0iOTkiIHkxPSI2OS41IiB4Mj0iMCIgeTI9IjY5LjUiLz48bGluZSBpZD0iWTgiIHgxPSI5OSIgeTE9Ijc5LjUiIHgyPSIwIiB5Mj0iNzkuNSIvPjxsaW5lIGlkPSJZOSIgeDE9Ijk5IiB5MT0iODkuNSIgeDI9IjAiIHkyPSI4OS41Ii8+PC9zdmc+"); | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="pad" width="1000" height="1000"></canvas> | |
<script type="module"> | |
const pad = document.getElementById("pad"); | |
const ctx = pad.getContext("2d"); | |
const px = new ImageData(new Uint8ClampedArray([0, 0, 0, 255]), 1, 1); | |
circle(pad.width / 2, pad.height / 2, 100); | |
/** | |
* Draw an ugly circle. | |
* | |
* @param {Number} midX - X ordinate of the circle's centre | |
* @param {Number} midY - Y ordinate of the circle's centre | |
* @param {Number} radX - Horizontal radius | |
* @param {Number} [radY=radX] - Vertical radius | |
* @param {Number} [angleStart=0] | |
* @param {Number} [angleEnd=360] | |
*/ | |
function circle(midX, midY, radX, radY = radX, angleStart = 0, angleEnd = 360){ | |
for(let θ = angleStart; θ < angleEnd; ++θ){ | |
const radians = θ * Math.PI / 180; | |
const x = Math.cos(radians) * radX; | |
const y = Math.sin(radians) * radY; | |
ctx.putImageData(px, midX + x, midY + y); | |
} | |
} | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
^ | |
(?: | |
// | |
| | |
(?<protocol>[^/#:?]*) : (?://)? | |
)? | |
(?: | |
(?=$|\s) | |
| | |
(?: | |
(?<auth> | |
(?<username>[^/:#?@]+) | |
(?::(?<password>[^@]+))? | |
@ | |
)? | |
(?<hostname>[^/:]+) | |
(?::(?<port>\d+))? | |
)? | |
(?<pathname> | |
/? | |
(?:[^/#?]+/)* | |
(?<filename>[^/#?]+)? | |
/? | |
) | |
(?<query>\?[^#]*)? | |
(?<fragment>#.*)? | |
$ | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment