From 80754fb4e504c746c16b7fd2edb6728e7fcc117a Mon Sep 17 00:00:00 2001 From: Jakub Jankiewicz Date: Tue, 14 Dec 2021 13:06:39 +0100 Subject: [PATCH] fix another edge case in #709 + refactor #717 * fix case when echo two lines with echo newline and flush false * refactor get_output_buffer function * rename clear_buffer --- CHANGELOG.md | 2 +- README.md | 2 +- __tests__/__snapshots__/terminal.spec.js.snap | 40 +++++++++++++++++++ __tests__/terminal.spec.js | 40 ++++++++++++++++++- js/jquery.terminal-2.29.5.js | 34 +++++++++++----- js/jquery.terminal-2.29.5.min.js | 4 +- js/jquery.terminal-src.js | 30 ++++++++++---- js/jquery.terminal.js | 34 +++++++++++----- js/jquery.terminal.min.js | 4 +- js/jquery.terminal.min.js.map | 2 +- 10 files changed, 155 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ade57a41..fd8a3fad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features * add `span` to xml formatting * allow to use class attribute in XML formatting (`span`, `link`, and `img`) -* new API methods `buffer_clean()` and `get_output_buffer()` [#717](https://github.com/jcubic/jquery.terminal/issues/717) +* new API methods `clear_buffer()` and `get_output_buffer()` [#717](https://github.com/jcubic/jquery.terminal/issues/717) ### Bugfix * fix bug on Android with GBoard keyboard [#693](https://github.com/jcubic/jquery.terminal/issues/693) * fix refresh when scrollbar appear (using `scrollbar-gutter`) diff --git a/README.md b/README.md index 4151ae58..3fecf7d4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![npm](https://img.shields.io/badge/npm-DEV-blue.svg)](https://www.npmjs.com/package/jquery.terminal) ![bower](https://img.shields.io/badge/bower-DEV-yellow.svg) [![Build and test](https://github.com/jcubic/jquery.terminal/actions/workflows/build.yaml/badge.svg?branch=devel&event=push)](https://github.com/jcubic/jquery.terminal/actions/workflows/build.yaml) -[![Coverage Status](https://coveralls.io/repos/github/jcubic/jquery.terminal/badge.svg?branch=devel&5507ee84c3031a012991aaf4bfcff76d)](https://coveralls.io/github/jcubic/jquery.terminal?branch=devel) +[![Coverage Status](https://coveralls.io/repos/github/jcubic/jquery.terminal/badge.svg?branch=devel&f1ed231cfa14ccdffd19a376c6e07da2)](https://coveralls.io/github/jcubic/jquery.terminal?branch=devel) ![downloads](https://img.shields.io/npm/dm/jquery.terminal.svg?style=flat) [![Paid Support](https://data.jsdelivr.com/v1/package/npm/jquery.terminal/badge?style=rounded)](https://www.jsdelivr.com/package/npm/jquery.terminal) [![](https://img.shields.io/badge/paid-support-354465.svg)](https://support.jcubic.pl/) diff --git a/__tests__/__snapshots__/terminal.spec.js.snap b/__tests__/__snapshots__/terminal.spec.js.snap index 43a883c0..25085db1 100644 --- a/__tests__/__snapshots__/terminal.spec.js.snap +++ b/__tests__/__snapshots__/terminal.spec.js.snap @@ -47,6 +47,26 @@ exports[`Terminal plugin events enter text text should appear and interpreter fu exports[`Terminal plugin jQuery Terminal methods generic import/export view should import view 1`] = `"
Hello World!
 foo\\">> foo
 bar\\">> bar
"`; +exports[`Terminal plugin jQuery Terminal methods output_buffer should return buffer with flush and newline 1`] = ` +"foo bar +lorem ipsum" +`; + +exports[`Terminal plugin jQuery Terminal methods output_buffer should return buffer with flush and newline 2`] = ` +"
foo bar
+
lorem ipsum
" +`; + +exports[`Terminal plugin jQuery Terminal methods output_buffer should return clear the buffer 1`] = ` +"foo bar +lorem ipsum" +`; + +exports[`Terminal plugin jQuery Terminal methods output_buffer should return clear the buffer 2`] = ` +"
foo bar
+
lorem ipsum
" +`; + exports[`Terminal plugin prompt should format prompt 1`] = `">>\\">>>> "`; exports[`Terminal plugin prompt should format prompt 2`] = `">>\\">>>> "`; @@ -568,3 +588,23 @@ Array [ "foobar  ba      br", ] `; + +exports[`extensions echo_newline should print mixed newline with !flush 1`] = ` +"foo, bar +baz, quux" +`; + +exports[`extensions echo_newline should print mixed newline with !flush 2`] = ` +Array [ + "foo, bar", + "baz, quux", +] +`; + +exports[`extensions echo_newline should print multiple !flush && !newline 1`] = `"foo, bar, baz, "`; + +exports[`extensions echo_newline should print multiple !flush && !newline 2`] = ` +Array [ + "foo, bar, baz, ", +] +`; diff --git a/__tests__/terminal.spec.js b/__tests__/terminal.spec.js index 78f731da..56a7c756 100644 --- a/__tests__/terminal.spec.js +++ b/__tests__/terminal.spec.js @@ -2712,8 +2712,19 @@ describe('extensions', function() { term.echo('bar, ', {newline: false, flush: false}); term.echo('baz, ', {newline: false, flush: false}); term.flush(); - expect(term.get_output()).toEqual('foo, bar, baz, '); - expect(output(term)).toEqual(['foo, bar, baz, ']); + expect(term.get_output()).toMatchSnapshot(); + expect(output(term)).toMatchSnapshot(); + }); + it('should print mixed newline with !flush', function() { + term.echo('foo, ', {newline: false, flush: false}); + term.echo('bar', {newline: false, flush: false}); + term.echo('', {flush: false}); + term.echo('baz, ', {newline: false, flush: false}); + term.echo('quux', {newline: false, flush: false}); + term.echo('', {flush: false}); + term.flush(); + expect(term.get_output()).toMatchSnapshot(); + expect(output(term)).toMatchSnapshot(); }); }); describe('autocomplete_menu', function() { @@ -5664,6 +5675,31 @@ describe('Terminal plugin', function() { expect(term.find("[data-index='1']").children().last().text()).toEqual(nbsp("ccc> !!!")); }); }); + describe('output_buffer', function() { + var term = $('
').terminal($.noop, {greetings: false}); + beforeEach(() => { + term.clear(); + }); + it('should return buffer with flush and newline', () => { + term.echo('foo ', {newline: false, flush: false}); + term.echo('bar', {flush: false}); + term.echo('lorem', {newline: false, flush: false}); + term.echo(' ipsum', {flush: false}); + expect(term.get_output_buffer()).toMatchSnapshot(); + expect(term.get_output_buffer({html: true})).toMatchSnapshot(); + }); + it('should return clear the buffer', () => { + term.echo('foo ', {newline: false, flush: false}); + term.echo('bar', {flush: false}); + term.echo('lorem', {newline: false, flush: false}); + term.echo(' ipsum', {flush: false}); + expect(term.get_output_buffer()).toMatchSnapshot(); + expect(term.get_output_buffer({html: true})).toMatchSnapshot(); + term.clear_buffer(); + expect(term.get_output()).toEqual(''); + expect(output(term)).toEqual([]); + }); + }); describe('last_index', function() { var term = $('
').terminal($.noop, {greetings: false}); it('should return proper index', function() { diff --git a/js/jquery.terminal-2.29.5.js b/js/jquery.terminal-2.29.5.js index f4564b54..85609263 100644 --- a/js/jquery.terminal-2.29.5.js +++ b/js/jquery.terminal-2.29.5.js @@ -41,7 +41,7 @@ * * broken image by Sophia Bai from the Noun Project (CC-BY) * - * Date: Mon, 13 Dec 2021 22:47:44 +0000 + * Date: Tue, 14 Dec 2021 12:05:15 +0000 */ /* global define, Map */ /* eslint-disable */ @@ -5116,7 +5116,7 @@ // ------------------------------------------------------------------------- $.terminal = { version: 'DEV', - date: 'Mon, 13 Dec 2021 22:47:44 +0000', + date: 'Tue, 14 Dec 2021 12:05:15 +0000', // colors from https://www.w3.org/wiki/CSS/Properties/color/keywords color_names: [ 'transparent', 'currentcolor', 'black', 'silver', 'gray', 'white', @@ -8889,6 +8889,7 @@ // ------------------------------------------------------------- clear: function() { if (fire_event('onClear') !== false) { + buffer.clear(); lines.clear(function(i) { return get_node(i); }); @@ -9916,6 +9917,7 @@ wrapper = $('
'); snapshot = []; } else if (first) { + first = false; appending_to_partial = true; wrapper = partial; } @@ -10907,29 +10909,41 @@ // ------------------------------------------------------------- get_output_buffer: function(options) { var settings = $.extend({ - render: true + html: false }, options); - var output = []; + var result = []; var append = false; buffer.forEach(function(data) { if (data) { if (is_function(data.finalize)) { append = !data.newline; } else { - console.log(data); + var output; + if (settings.html) { + output = data.line; + } else { + output = data.raw; + } if (append) { - var last = output.length - 1; - output[last] += data.raw; + var last = result.length - 1; + result[last] += output; } else { - output.push(data.raw); + result.push(output); } } } }); - return output; + if (settings.html) { + return result.map(function(line) { + return '
' + line + '
'; + }).join('\n'); + } + return result.join('\n'); }, // ------------------------------------------------------------- - buffer_clean: function() { + // :: clear flush buffer + // ------------------------------------------------------------- + clear_buffer: function() { buffer.clear(); return self; } diff --git a/js/jquery.terminal-2.29.5.min.js b/js/jquery.terminal-2.29.5.min.js index f3bea652..9f6450dd 100644 --- a/js/jquery.terminal-2.29.5.min.js +++ b/js/jquery.terminal-2.29.5.min.js @@ -41,7 +41,7 @@ * * broken image by Sophia Bai from the Noun Project (CC-BY) * - * Date: Mon, 13 Dec 2021 22:47:44 +0000 + * Date: Tue, 14 Dec 2021 12:05:15 +0000 */ -(function(e){var D=function(){if(!D.cache.hasOwnProperty(arguments[0])){D.cache[arguments[0]]=D.parse(arguments[0])}return D.format.call(null,D.cache[arguments[0]],arguments)};D.format=function(e,t){var n=1,r=e.length,i="",u,a=[],o,s,l,f,c,p;for(o=0;o>>0;break;case"x":u=u.toString(16);break;case"X":u=u.toString(16).toUpperCase();break}u=/[def]/.test(l[8])&&l[3]&&u>=0?" +"+u:u;c=l[4]?l[4]==="0"?"0":l[4].charAt(1):" ";p=l[6]-String(u).length;f=l[6]?d(c,p):"";a.push(l[5]?u+f:f+u)}}return a.join("")};D.cache={};D.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null){r.push(n[0])}else if((n=/^\x25{2}/.exec(t))!==null){r.push("%")}else if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))!==null){if(n[2]){i|=1;var u=[],a=n[2],o=[];if((o=/^([a-z_][a-z_\d]*)/i.exec(a))!==null){u.push(o[1]);while((a=a.slice(o[0].length))!==""){if((o=/^\.([a-z_][a-z_\d]*)/i.exec(a))!==null){u.push(o[1])}else if((o=/^\[(\d+)\]/.exec(a))!==null){u.push(o[1])}else{throw"[sprintf] huh?"}}}else{throw"[sprintf] huh?"}n[2]=u}else{i|=2}if(i===3){throw"[sprintf] mixing positional and named placeholders is not (yet) supported"}r.push(n)}else{throw"[sprintf] huh?"}t=t.slice(n[0].length)}return r};var t=function(e,t,n){n=t.slice(0);n.splice(0,0,e);return D.apply(null,n)};function m(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}function d(e,t){for(var n=[];t>0;n[--t]=e){}return n.join("")}e.sprintf=D;e.vsprintf=t})(typeof global!=="undefined"?global:self||window);(function(r,i){var n;if(typeof window!=="undefined"){n=window}else if(typeof self!=="undefined"){n=self}else if(typeof global!=="undefined"){n=global}else{throw new Error("Unknow context")}if(typeof define==="function"&&define.amd){define(["jquery","wcwidth"],function(e,t){r(e,t,n);return e})}else if(typeof module==="object"&&module.exports){module.exports=function(e,t,n){if(t===i){if(typeof window!=="undefined"){t=require("jquery")}else{t=require("jquery")(e)}}if(n===i){n=require("wcwidth")}r(t,n,e);return t}}else{r(n.jQuery,n.wcwidth,n)}})(function($,wcwidth,root,undefined){"use strict";function debug(e){if(false){console.log(e)}}function DelayQueue(){var t=$.Callbacks();var n=false;this.resolve=function(){t.fire();n=true};this.add=function(e){if(n){e()}else{t.add(e)}}}$.omap=function(n,r){var i={};$.each(n,function(e,t){i[e]=r.call(n,e,t)});return i};$.fn.text_length=function(){return this.map(function(){return $(this).text().length}).get().reduce(function(e,t){return e+t},0)};var Clone={clone_object:function(e){var t={};if(typeof e==="object"){if($.isArray(e)){return this.clone_array(e)}else if(e===null){return e}else{for(var n in e){if($.isArray(e[n])){t[n]=this.clone_array(e[n])}else if(typeof e[n]==="object"){t[n]=this.clone_object(e[n])}else{t[n]=e[n]}}}}return t},clone_array:function(e){if(!is_function(Array.prototype.map)){throw new Error("Your browser don't support ES5 array map "+"use es5-shim")}return e.slice(0).map(function(e){if(typeof e==="object"){return this.clone_object(e)}else{return e}}.bind(this))}};var clone=function(e){return Clone.clone_object(e)};if("Map"in root&&!("clear"in Map.prototype)){Map.prototype.clear=function(){this.forEach(function(e,t,n){n.delete(t)})}}var localStorage;(function(){var e=function(){try{var e="test",t=window.localStorage;t.setItem(e,"1");t.removeItem(e);return true}catch(e){return false}};var t=function(){try{document.cookie.split(";");return true}catch(e){return false}};var n=e();function r(e,t){var n;if(typeof e==="string"&&typeof t==="string"){localStorage[e]=t;return true}else if(typeof e==="object"&&typeof t==="undefined"){for(n in e){if(e.hasOwnProperty(n)){localStorage[n]=e[n]}}return true}return false}function i(e,t){var n,r,i;n=new Date;n.setTime(n.getTime()+31536e6);r="; expires="+n.toGMTString();if(typeof e==="string"&&typeof t==="string"){document.cookie=e+"="+t+r+"; path=/";return true}else if(typeof e==="object"&&typeof t==="undefined"){for(i in e){if(e.hasOwnProperty(i)){document.cookie=i+"="+e[i]+r+"; path=/"}}return true}return false}function u(e){return localStorage[e]}function a(e){var t,n,r,i;t=e+"=";n=document.cookie.split(";");for(r=0;r=i||t<0||m&&n>=s}function y(){var e=$();if(_(e)){return b(e)}f=setTimeout(y,g(e))}function b(e){f=undefined;if(d&&a){return h(e)}a=o=undefined;return l}function C(){if(f!==undefined){clearTimeout(f)}p=0;a=c=o=f=undefined}function F(){return f===undefined?l:b($())}function w(){var e=$(),t=_(e);a=arguments;o=this;c=e;if(t){if(f===undefined){return v(c)}if(m){f=setTimeout(y,i);return h(c)}}if(f===undefined){f=setTimeout(y,i)}return l}w.cancel=C;w.flush=F;return w}}();var jQuery=$;(function(e){jQuery.fn.extend({everyTime:function(e,t,n,r,i){return this.each(function(){jQuery.timer.add(this,e,t,n,r,i)})},oneTime:function(e,t,n){return this.each(function(){jQuery.timer.add(this,e,t,n,1)})},stopTime:function(e,t){return this.each(function(){jQuery.timer.remove(this,e,t)})}});jQuery.extend({timer:{guid:1,global:{},regex:/^([0-9]+)\s*(.*s)?$/,powers:{ms:1,cs:10,ds:100,s:1e3,das:1e4,hs:1e5,ks:1e6},timeParse:function(e){if(e===undefined||e===null){return null}var t=this.regex.exec(jQuery.trim(e.toString()));if(t[2]){var n=parseInt(t[1],10);var r=this.powers[t[2]]||1;return n*r}else{return e}},add:function(e,t,n,r,i,u){var a=0;if(jQuery.isFunction(n)){if(!i){i=r}r=n;n=t}t=jQuery.timer.timeParse(t);if(typeof t!=="number"||isNaN(t)||t<=0){return}if(i&&i.constructor!==Number){u=!!i;i=0}i=i||0;u=u||false;if(!e.$timers){e.$timers={}}if(!e.$timers[n]){e.$timers[n]={}}r.$timerID=r.$timerID||this.guid++;var o=function(){if(u&&o.inProgress){return}o.inProgress=true;if(++a>i&&i!==0||r.call(e,a)===false){jQuery.timer.remove(e,n,r)}o.inProgress=false};o.$timerID=r.$timerID;if(!e.$timers[n][r.$timerID]){e.$timers[n][r.$timerID]=setInterval(o,t)}if(!this.global[n]){this.global[n]=[]}this.global[n].push(e)},remove:function(e,t,n){var r=e.$timers,i;if(r){if(!t){for(var u in r){if(r.hasOwnProperty(u)){this.remove(e,u,n)}}}else if(r[t]){if(n){if(n.$timerID){clearInterval(r[t][n.$timerID]);delete r[t][n.$timerID]}}else{for(var a in r[t]){if(r[t].hasOwnProperty(a)){clearInterval(r[t][a]);delete r[t][a]}}}for(i in r[t]){if(r[t].hasOwnProperty(i)){break}}if(!i){i=null;delete r[t]}}for(i in r){if(r.hasOwnProperty(i)){break}}if(!i){e.$timers=null}}}}});if(/(msie) ([\w.]+)/.exec(navigator.userAgent.toLowerCase())){e(window).one("unload",function(){var e=jQuery.timer.global;for(var t in e){if(e.hasOwnProperty(t)){var n=e[t],r=n.length;while(--r){jQuery.timer.remove(n[r],t)}}}})}})(jQuery);(function(f){if(!String.prototype.split.toString().match(/\[native/)){return}var c=String.prototype.split,p=/()??/.exec("")[1]===f,n;n=function(e,t,n){if(Object.prototype.toString.call(t)!=="[object RegExp]"){return c.call(e,t,n)}var r=[],i=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.extended?"x":"")+(t.sticky?"y":""),u=0,a,o,s,l;t=new RegExp(t.source,i+"g");e+="";if(!p){a=new RegExp("^"+t.source+"$(?!\\s)",i)}n=n===f?-1>>>0:n>>>0;while(o=t.exec(e)){s=o.index+o[0].length;if(s>u){r.push(e.slice(u,o.index));if(!p&&o.length>1){o[0].replace(a,function(){for(var e=1;e1&&o.index=n){break}}if(t.lastIndex===o.index){t.lastIndex++}}if(u===e.length){if(l||!t.test("")){r.push("")}}else{r.push(e.slice(u))}return r.length>n?r.slice(0,n):r};String.prototype.split=function(e,t){return n(this,e,t)};return n})();$.fn.caret=function(e){var t=this[0];var n=t.contentEditable==="true";if(arguments.length===0){if(window.getSelection){if(n){if(!this.is(":focus")){t.focus()}var r=window.getSelection().getRangeAt(0),i=r.cloneRange();i.selectNodeContents(t);i.setEnd(r.endContainer,r.endOffset);return i.toString().length}return t.selectionStart}if(document.selection){t.focus();if(n){var r=document.selection.createRange(),i=document.body.createTextRange();i.moveToElementText(t);i.setEndPoint("EndToEnd",r);return i.text.length}var e=0,u=t.createTextRange(),i=document.selection.createRange().duplicate(),a=i.getBookmark();u.moveToBookmark(a);while(u.moveStart("character",-1)!==0)e++;return e}return 0}if(e===-1)e=this[n?"text":"val"]().length;if(window.getSelection){if(n){if(!this.is(":focus")){t.focus()}var o=window.getSelection();o.collapse(o.focusNode,e)}else t.setSelectionRange(e,e)}else if(document.body.createTextRange){var u=document.body.createTextRange();u.moveToElementText(t);u.moveStart("character",e);u.collapse(true);u.select()}if(!n&&!this.is(":focus")){t.focus()}return e};function make_callback_plugin(e){var s=$.extend({init:$.noop,destroy:$.noop,name:"event"},e);return function(r,i){var u=arguments.length===0;var a=arguments[0]==="unbind";if(!u&&!a&&!is_function(r)){throw new Error("Invalid argument, it need to a function or string "+'"unbind" or no arguments.')}if(a){r=is_function(arguments[1])?arguments[1]:null}var o="callbacks_"+s.name;return this.each(function(){var t=$(this);var n;function e(e){n.fireWith(t,[e])}if(u||a){n=t.data(o);if(u){n&&n.fire()}else{if(r&&n){n.remove(r);if(!n.has()){n=null}}else{n=null}if(!n){t.removeData(o);s.destroy.call(this,e,i)}}}else if(t.data(o)){$(this).data(o).add(r)}else{n=$.Callbacks();n.add(r);t.data(o,n);s.init.call(this,e,i)}})}}$.fn.resizer=make_callback_plugin({name:"resize",init:function(e,t){var n=$.extend({prefix:""},t);var r=$(this);var i;var u=true;if(r.is("body")){$(window).on("resize.resizer",e)}else if(window.ResizeObserver){i=new ResizeObserver(function(){if(!u){e()}u=false});i.observe(this);r.data("observer",i)}else{var a=$("