Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ansi-escape): add ansi escape module (#1)
* feat(ansi-escape): add ansi escape module * update readme
- Loading branch information
Showing
6 changed files
with
348 additions
and
1 deletion.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './packages/ansi-escape/mod.ts'; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Cliffy - ANSI Escape | ||
|
||
ANSI escape module for handling cli cursor, erase output and scroll window. | ||
|
||
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/c4spar/deno-cliffy?logo=github) ![GitHub Release Date](https://img.shields.io/github/release-date/c4spar/deno-cliffy?logo=github) | ||
|
||
![Build Status](https://github.com/c4spar/deno-cliffy/workflows/ci/badge.svg?branch=master) ![Deno version](https://img.shields.io/badge/deno-v0.41.0|v0.40.0|v0.39.0-green?logo=deno) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/c4spar/deno-cliffy?logo=github) ![GitHub issues](https://img.shields.io/github/issues/c4spar/deno-cliffy?logo=github) ![GitHub licence](https://img.shields.io/github/license/c4spar/deno-cliffy?logo=github) | ||
|
||
## Usage | ||
|
||
```typescript | ||
#!/usr/bin/env -S deno | ||
|
||
import { AnsiEscape } from 'https://deno.land/x/cliffy/ansi-escape.ts'; | ||
|
||
AnsiEscape.from( Deno.stdout ) | ||
// Hide cursor: | ||
.cursorHide() | ||
// Show cursor: | ||
.cursorShow() | ||
// Erase current line: | ||
.eraseLine() | ||
// Erase three line's up: | ||
.eraseLines( 3 ) | ||
// Scroll two line's up: | ||
.scrollUp( 2 ) | ||
// Scroll one line down: | ||
.scrollDown() | ||
// ... | ||
``` | ||
|
||
## License | ||
|
||
[MIT](LICENSE) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import { encode } from 'https://deno.land/std@v0.41.0/encoding/utf8.ts'; | ||
import { cursor, erase, image, ImageOptions, link, scroll } from './csi.ts'; | ||
|
||
export class AnsiEscape { | ||
|
||
/** Create instance from file. */ | ||
public static from( file: Deno.File ): AnsiEscape { | ||
return new this( file ); | ||
} | ||
|
||
protected constructor( protected file: Deno.File ) {} | ||
|
||
/** Write to file. */ | ||
public write( code: string ): this { | ||
this.file.writeSync( encode( code ) ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Cursor | ||
*/ | ||
|
||
/** Move cursor to x, y, counting from the top left corner. */ | ||
public cursorTo( x: number, y?: number ) { | ||
this.write( cursor.to( x, y ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor by offset. */ | ||
public cursorMove( x: number, y: number ) { | ||
this.write( cursor.move( x, y ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor up by n lines. */ | ||
public cursorUp( count: number = 1 ) { | ||
this.write( cursor.up( count ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor down by n lines. */ | ||
public cursorDown( count: number = 1 ) { | ||
this.write( cursor.down( count ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor right by n lines. */ | ||
public cursorForward( count: number = 1 ) { | ||
this.write( cursor.forward( count ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor left by n lines. */ | ||
public cursorBackward( count: number = 1 ) { | ||
this.write( cursor.backward( count ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor to the beginning of the line n lines down. */ | ||
public cursorNextLine( count: number = 1 ) { | ||
this.write( cursor.nextLine( count ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor to the beginning of the line n lines up. */ | ||
public cursorPrevLine( count: number = 1 ) { | ||
this.write( cursor.prevLine( count ) ); | ||
return this; | ||
} | ||
|
||
/** Move cursor to first column of current row. */ | ||
public cursorLeft() { | ||
this.write( cursor.left ); | ||
return this; | ||
} | ||
|
||
/** Hide cursor. */ | ||
public cursorHide() { | ||
this.write( cursor.hide ); | ||
return this; | ||
} | ||
|
||
/** Show cursor. */ | ||
public cursorShow() { | ||
this.write( cursor.show ); | ||
return this; | ||
} | ||
|
||
/** Save cursor. */ | ||
public cursorSave() { | ||
this.write( cursor.save ); | ||
return this; | ||
} | ||
|
||
/** Restore cursor. */ | ||
public cursorRestore() { | ||
this.write( cursor.restore ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Scroll | ||
*/ | ||
|
||
/** Scroll window up by n lines. */ | ||
public scrollUp( count: number = 1 ) { | ||
this.write( scroll.up( count ) ); | ||
return this; | ||
} | ||
|
||
/** Scroll window down by n lines. */ | ||
public scrollDown( count: number = 1 ) { | ||
this.write( scroll.down( count ) ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Erase | ||
*/ | ||
|
||
/** Clear screen. */ | ||
public eraseScreen() { | ||
this.write( erase.screen ); | ||
return this; | ||
} | ||
|
||
/** Clear screen up. */ | ||
public eraseUp( count: number = 1 ) { | ||
this.write( erase.up( count ) ); | ||
return this; | ||
} | ||
|
||
/** Clear screen down. */ | ||
public eraseDown( count: number = 1 ) { | ||
this.write( erase.down( count ) ); | ||
return this; | ||
} | ||
|
||
/** Clear current line. */ | ||
public eraseLine() { | ||
this.write( erase.line ); | ||
return this; | ||
} | ||
|
||
/** Clear to line end. */ | ||
public eraseLineEnd() { | ||
this.write( erase.lineEnd ); | ||
return this; | ||
} | ||
|
||
/** Clear to line start. */ | ||
public eraseLineStart() { | ||
this.write( erase.lineStart ); | ||
return this; | ||
} | ||
|
||
/** Clear n line's up. */ | ||
public eraseLines( count: number ) { | ||
this.write( erase.lines( count ) ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Style | ||
*/ | ||
|
||
/** Render link. */ | ||
public link( text: string, url: string ) { | ||
this.write( link( text, url ) ); | ||
return this; | ||
} | ||
|
||
/** Render image. */ | ||
public image( buffer: Uint8Array, options?: ImageOptions ) { | ||
this.write( image( buffer, options ) ); | ||
return this; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import * as base64 from 'https://deno.land/x/base64@v0.2.0/mod.ts'; | ||
|
||
export const ESC = '\x1B'; | ||
export const CSI = `${ ESC }[`; | ||
export const OSC = `${ ESC }]`; | ||
export const BEL = '\u0007'; | ||
const SEP = ';'; | ||
|
||
export const cursor = { | ||
/** Move cursor to x, y, counting from the top left corner. */ | ||
to( x: number, y?: number ) { | ||
if ( typeof y !== 'number' ) { | ||
return `${ CSI }${ x }G`; | ||
} | ||
return `${ CSI }${ y };${ x }H`; | ||
}, | ||
/** Move cursor by offset. */ | ||
move( x: number, y: number ) { | ||
let ret = ''; | ||
|
||
if ( x < 0 ) { | ||
ret += `${ CSI }${ -x }D`; | ||
} else if ( x > 0 ) { | ||
ret += `${ CSI }${ x }C`; | ||
} | ||
|
||
if ( y < 0 ) { | ||
ret += `${ CSI }${ -y }A`; | ||
} else if ( y > 0 ) { | ||
ret += `${ CSI }${ y }B`; | ||
} | ||
|
||
return ret; | ||
}, | ||
/** Move cursor up by n lines. */ | ||
up: ( count: number = 1 ) => `${ CSI }${ count }A`, | ||
/** Move cursor down by n lines. */ | ||
down: ( count: number = 1 ) => `${ CSI }${ count }B`, | ||
/** Move cursor right by n lines. */ | ||
forward: ( count: number = 1 ) => `${ CSI }${ count }C`, | ||
/** Move cursor left by n lines. */ | ||
backward: ( count: number = 1 ) => `${ CSI }${ count }D`, | ||
/** Move cursor to the beginning of the line n lines down. */ | ||
nextLine: ( count: number = 1 ) => `${ CSI }E`.repeat( count ), | ||
/** Move cursor to the beginning of the line n lines up. */ | ||
prevLine: ( count: number = 1 ) => `${ CSI }F`.repeat( count ), | ||
/** Move cursor to first column of current row. */ | ||
left: `${ CSI }G`, | ||
/** Hide cursor. */ | ||
hide: `${ CSI }?25l`, | ||
/** Show cursor. */ | ||
show: `${ CSI }?25h`, | ||
/** Save cursor. */ | ||
save: `${ ESC }7`, | ||
/** Restore cursor. */ | ||
restore: `${ ESC }8` | ||
}; | ||
|
||
export const scroll = { | ||
/** Scroll window up by n lines. */ | ||
up: ( count: number = 1 ) => `${ CSI }S`.repeat( count ), | ||
/** Scroll window down by n lines. */ | ||
down: ( count: number = 1 ) => `${ CSI }T`.repeat( count ) | ||
}; | ||
|
||
export const erase = { | ||
/** Clear screen. */ | ||
screen: `${ CSI }2J`, | ||
/** Clear screen up. */ | ||
up: ( count: number = 1 ) => `${ CSI }1J`.repeat( count ), | ||
/** Clear screen down. */ | ||
down: ( count: number = 1 ) => `${ CSI }0J`.repeat( count ), | ||
/** Clear current line. */ | ||
line: `${ CSI }2K`, | ||
/** Clear to line end. */ | ||
lineEnd: `${ CSI }0K`, | ||
/** Clear to line start. */ | ||
lineStart: `${ CSI }1K`, | ||
/** Clear n line's up. */ | ||
lines( count: number ) { | ||
let clear = ''; | ||
for ( let i = 0; i < count; i++ ) { | ||
clear += this.line + ( i < count - 1 ? cursor.up() : '' ); | ||
} | ||
clear += cursor.left; | ||
return clear; | ||
} | ||
}; | ||
|
||
/** Render link. */ | ||
export const link = ( text: string, url: string ) => [ | ||
OSC, | ||
'8', | ||
SEP, | ||
SEP, | ||
url, | ||
BEL, | ||
text, | ||
OSC, | ||
'8', | ||
SEP, | ||
SEP, | ||
BEL | ||
].join( '' ); | ||
|
||
export interface ImageOptions { | ||
width?: number, | ||
height?: number, | ||
preserveAspectRatio?: boolean | ||
} | ||
|
||
/** Render image. */ | ||
export const image = ( buffer: Uint8Array, options?: ImageOptions ) => { | ||
|
||
let ret = `${ OSC }1337;File=inline=1`; | ||
|
||
if ( options?.width ) { | ||
ret += `;width=${ options.width }`; | ||
} | ||
|
||
if ( options?.height ) { | ||
ret += `;height=${ options.height }`; | ||
} | ||
|
||
if ( options?.preserveAspectRatio === false ) { | ||
ret += ';preserveAspectRatio=0'; | ||
} | ||
|
||
return ret + ':' + base64.fromUint8Array( buffer ) + BEL; | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './lib/csi.ts'; | ||
export * from './lib/ansi-escape.ts'; |