Skip to content
99 changes: 59 additions & 40 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import fs from 'fs';
import readline from 'readline';
import properLockFile from 'proper-lockfile';
import ansi from 'ansi-escapes';
import boring from 'boring';
import { inspect } from 'util';
Expand All @@ -15,22 +14,31 @@ import loadLanguages from './load-languages.js';

import Screen from './screen.js';

const tabSpaces = 2;

const argv = boring();

const filename = argv._[0];
argv.stdout = argv.stdout || !!argv._[1] || !process.stdout.isTTY;

const stdin = process.stdin;
const stdout = process.stdout;
let stdout;
if(argv.stdout === true) {
stdout = process.stderr;
} else {
stdout = process.stdout;
}
stdin.setRawMode(true);
readline.emitKeypressEvents(stdin);
const screen = new Screen({
stdout,
log
});

const tabSpaces = 2;

const argv = boring();

const filename = argv._[0];

if (!filename && !argv['debug-keycodes']) {
if ((!argv.stdout && !filename && !argv['debug-keycodes']) || (argv.stdout && !process.stderr.isTTY)) {
if(argv.stdout && !process.stderr.isTTY) {
process.stderr.write('Error: Unable to display editor UI over stderr. When piping or using --stdout, stderr must be a TTY.\n\n');
}
usage();
}

Expand All @@ -42,8 +50,7 @@ fs.mkdirSync(stateFolder, { recursive: true });
const logFile = fs.createWriteStream(`${stateFolder}/log.txt`, 'utf8');

const clipboard = clipboardFactory({
stateFolder,
lock
stateFolder
});

const hintStack = [
Expand Down Expand Up @@ -95,12 +102,13 @@ stdin.on('keypress', (c, k) => {
// ignore occasional undefined keys on mac
return;
}
if (k.shift) {
k.name = `shift-${k.name}`;
}
if (k.ctrl) {
k.name = `control-${k.name}`;
}
let prefix = '';
if (k.ctrl) prefix += 'control-';
if (k.meta) prefix += 'meta-';
if (k.shift) prefix += 'shift-';

k.name = prefix + k.name;

if ((k.sequence.charCodeAt(0) === 27) && (k.sequence.charCodeAt(1) === 27)) {
// readline isn't quite smart enough on its own to do the right thing if
// ESC is followed quickly by an arrow key, but gives us enough information
Expand Down Expand Up @@ -146,6 +154,9 @@ while (true) {
}

function shortFilename(prompt) {
if (!filename || (argv.stdout && !fs.existsSync(filename))) {
return '<stdout>';
}
return filename.split('/').pop().substring(0, stdout.columns - (prompt || '').length - 5);
}

Expand All @@ -171,25 +182,22 @@ function log(...args) {
}
}

function lock(filename) {
return properLockFile(filename, {
retries: {
retries: 30,
factor: 1,
minTimeout: 1000,
maxTimeout: 1000
},
// Avoid chicken and egg problem when the file does not exist yet
realpath: false
});
}

function loadFile() {
if (!fs.existsSync(filename)) {
let content;
if(argv.stdout) {
if(!!argv._[1]) {
content = argv._[1].split('\n').map(line => [...line]);
}
if(!filename && !content) {
return false;
}
}
if (!filename || !fs.existsSync(filename)) {
return false;
} else {
// Emoji-safe split by character (split('') is not safe)
content = fs.readFileSync(filename, 'utf8').split('\n').map(line => [...line]);
}
// Emoji-safe split by character (split('') is not safe)
const content = fs.readFileSync(filename, 'utf8').split('\n').map(line => [...line]);
if (!content.length) {
content.push([]);
}
Expand All @@ -202,6 +210,8 @@ function newFile() {
}

function saveFile() {
if(argv.stdout && (filename == null || !fs.existsSync(filename)))
return;
fs.writeFileSync(filename, getText(editor.chars));
}

Expand All @@ -210,6 +220,12 @@ function getText(chars) {
}

async function closeEditor() {
if(argv.stdout){
stdout.write(ansi.clearScreen);
console.log(getText(editor.chars));
process.exit(0);
}

const text = getText(editor.chars);
if (text !== originalText) {
if (await confirm('Save before exiting? [Y/n]', true)) {
Expand Down Expand Up @@ -249,10 +265,13 @@ function usage() {

function resize() {
screen.resize();
editor.resize(process.stdout.columns, process.stdout.rows - 3);
editor.resize(stdout.columns, stdout.rows - 3);
}

function guessLanguage(filename) {
if(!filename) {
return languages.default;
}
const matches = filename.match(/\.([^\.]+)$/);
if (matches) {
const found = Object.values(languages).find(language => language.extensions.includes(matches[1]));
Expand All @@ -265,12 +284,12 @@ function status(prompt) {
const hints = hintStack[hintStack.length - 1];
const width = Math.max(...hints.map(s => s.length)) + 2;
let col = 0;
let row = process.stdout.rows - 2;
let row = stdout.rows - 2;
for (const hint of hints) {
if (col + width >= process.stdout.columns) {
if (col + width >= stdout.columns) {
fillRest();
row++;
if (row >= process.stdout.rows) {
if (row >= stdout.rows) {
break;
}
}
Expand All @@ -279,7 +298,7 @@ function status(prompt) {
}
col += width;
}
while (row < process.stdout.rows) {
while (row < stdout.rows) {
for (let sx = col; (sx < screen.width); sx++) {
screen.set(sx, row, ' ');
}
Expand All @@ -288,9 +307,9 @@ function status(prompt) {
}
const left = `${editor.row + 1} ${editor.col + 1} ${shortFilename()}`;
const right = (prompt !== false) ? prompt : '';
const s = left + ' '.repeat(process.stdout.columns - 1 - right.length - left.length) + right;
const s = left + ' '.repeat(stdout.columns - 1 - right.length - left.length) + right;
for (let i = 0; (i < s.length); i++) {
screen.set(i, process.stdout.rows - 3, s.charAt(i));
screen.set(i, stdout.rows - 3, s.charAt(i));
}
function fillRest() {
while (col < screen.width) {
Expand Down
33 changes: 33 additions & 0 deletions bin/tome.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env sh

scriptroot=$(dirname "$(realpath "$0")")
real_tome="$scriptroot/tome.js"

case " $* " in
*" --stdout "*) has_stdout=1 ;;
*) has_stdout=0 ;;
esac

args=
for arg in "$@"; do
[ "$arg" = "--stdout" ] || args="$args \"$arg\""
done

eval "set -- $args"

# Detect presence of --stdout among arguments and handle stdin accordingly
if [ -t 0 ]; then
if [ "$has_stdout" -eq 1 ]; then
exec "$real_tome" "$1" --stdout 2>/dev/tty
else
exec "$real_tome" "$1"
fi
else
input_data=$(cat)
if [ "$has_stdout" -eq 1 ]; then
exec "$real_tome" "$1" "$input_data" --stdout 2>/dev/tty < /dev/tty
else
exec "$real_tome" "$1" "$input_data" < /dev/tty
fi
fi

14 changes: 13 additions & 1 deletion clipboard.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
"use strict";

import fs from 'fs';
import properLockFile from 'proper-lockfile';

export default ({
stateFolder,
lock
lock = function lock(filename) {
return properLockFile(filename, {
retries: {
retries: 30,
factor: 1,
minTimeout: 1000,
maxTimeout: 1000
},
// Avoid chicken and egg problem when the file does not exist yet
realpath: false
});
}
}) => {
const clipboardFile = `${stateFolder}/clipboard.json`;
const clipboardLockFile = `${clipboardFile}.lock`;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"url": "https://github.com/boutell/tome/issues"
},
"bin": {
"tome": "./bin/tome.js"
"tome": "./bin/tome.sh"
},
"homepage": "https://github.com/boutell/tome#readme",
"dependencies": {
Expand Down
4 changes: 2 additions & 2 deletions screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export default class Screen {
}
allocate() {
const data = [];
this.width = process.stdout.columns;
this.height = process.stdout.rows;
this.width = this.stdout.columns;
this.height = this.stdout.rows;
for (let row = 0; (row < this.height); row++) {
const cells = [];
for (let col = 0; (col < this.width); col++) {
Expand Down