vue-command开源项目

我要开发同款
匿名用户2021年11月09日
46阅读
开发技术SHELL
所属分类终端/远程登录、应用工具
授权协议MIT License

作品详情

vue-command

Afullyworking,mostfeature-richVue.jsterminalemulator.Seethedemoandcheckthedemosourcecode.

FeaturesParsesargumentswithgetoptsSupportsasynchronouscommandsBrowsehistory(with↑/↓)Autocompletionresolver(with↹)CustomizeterminalwithslotsSearchhistory(withCtrl+r)Installation$npminstallvue-command--saveUsage

Let'sstartwithaverysimpleexample.Wewanttosend"Helloworld"toStdoutwhenenteringhello-world.

<template><vue-command:commands="commands"/></template><script>importVueCommand,{createStdout}from'vue-command'import'vue-command/dist/vue-command.css'exportdefault{components:{VueCommand},data:()=>({commands:{'hello-world':()=>createStdout('Helloworld')}})}</script>

Nowamorecomplexone.Let'sassumewewanttobuildtheNanoeditoravailableinmanyshells.

WewillusetheprovidedenvironmentvariabletomakesuretheeditorisonlyvisiblewhenthiscommandisexecutingandinjectafunctioncalledterminatetotelltheterminalthatthecommandhasbeenfinishedwhentheuserentersCtrl+x.Furthermore,weinjectthesetIsFullscreenfunctiontoswitchtheterminalintofullscreenmode.

<template><divv-if="environment.isExecuting"><textarearef="nano"@keydown.ctrl.88="terminate">Thisisatexteditor!PressCtrl+xtoleave.</textarea></div></template><script>exportdefault{inject:['setIsFullscreen','terminate'],created(){this.setIsFullscreen(true)},mounted(){this.$refs.nano.focus()}}</script>

Nowthecommandhastoreturnthecomponent.

<template><vue-command:commands="commands"/></template><script>importVueCommandfrom'vue-command'import'vue-command/dist/vue-command.css'importNanoEditorfrom'@/components/NanoEditor.vue'exportdefault{components:{VueCommand},data:()=>({commands:{nano:()=>NanoEditor}})}</script>Properties

Therearetwotypesofcommands:Built-inandregularones.Inmostcasesregularcommandsareappropriate.Built-incommandsprovidehigherflexibility,seesectionBuilt-informoreinformation.

Somepropertiescanbechangedbytheterminal,therefore,thesyncmodifierhastobeadded.

PropertyTypeDefaultSyncDescriptionautocompletion-resolverFunctionnullNoSeeAutocompletionresolverbuilt-inObject{}NoSeeBuilt-insectioncommandsObject{}NoSeeCommandssectioncursorNumber0YesSetstheStdincursorpositionevent-listenersArray[EVENT_LISTENERS.autocomplete,EVENT_LISTENERS.history,EVENT_LISTENERS.search]NoSeeEventlistenerssectionexecutedSetnewSet()YesExecutedprograms,see"Overwritingexecutedfunctions"help-textStringTypehelpNoSetstheplaceholderhelp-timeoutNumber4000NoSetstheplaceholdertimeouthide-barBooleanfalseNoHidesthebarhide-promptBooleanfalseNoHidestheprompthide-titleBooleanfalseNoHidesthetitlehistoryArray[]YesExecutedcommandsintroStringFastenyourseatbelts!NoSetstheintrois-fullscreenBooleanfalseYesSetstheterminalfullscreenmodeis-in-progressBooleanfalseYesSetstheterminalprogressstatusnot-foundStringnotfoundNoSetsthecommandnotfoundtextparser-optionsObject{}NoSetstheparseroptionspointerNumber0YesSetsthecommandpointerpromptString~neil@moon:#NoSetsthepromptshow-helpBooleanfalseNoShowstheplaceholdershow-introBooleanfalseNoShowstheintrostdinString''YesSetsthecurrentStdintitleStringneil@moon:~NoSetsthetitleCommands

commandsmustbeanobjectcontainingkey-valuepairswherekeyisthecommandandthevalueisafunctionthatwillbecalledwiththegetopsarguments.ThefunctioncanreturnaPromiseandmustreturnorresolveaVue.jscomponent.Toreturnstringsornothinguseoneoftheconvenienthelpermethods:

FunctionDescriptioncreateStdout(content:String,isInnerText:Boolean,isEscapeHtml:Boolean,name:String,...mixins:Array):ObjectReturnsaStdoutcomponentcontainingaspanelementwithgiveninnercontentcreateStderr(content:String,isEscapeHtml:Boolean,name:String,...mixins:Array):ObjectReturnsaStderrcomponentcontainingaspanelementwithgiveninnercontentcreateDummyStdout(name:String,...mixins:Array):ObjectReturnsadummyStdouttoshowaStdin

Helpermethodscanbeimportedbyname:

import{createStdout,createStderr,createDummyStdout}from'vue-command'

Ifnoneofthehelpermethodsisused,thecommandhastobemanuallyterminatedinsidethecomponent.Nexttoterminationit'spossibletoinjectthefollowingfunctionstomanipulatetheterminalorsignalanevent:

FunctionDescriptionemitExecuteEmitcommandexecutioneventemitExecutedEmitcommandexecutedeventemitInput(input:String)EmitthecurrentinputsetCursor(cursor:Number)SetcursorpositionsetIsFullscreen(isFullscreen:Boolean)ChangeiftheterminalisinfullscreenmodesetIsInProgress(isInProgress:Boolean)ChangeiftheterminalisinprogresssetPointer(pointer:Number)SetcommandhistorypointersetStdin(stdin:String)SetthecurrentStdinterminateExecutescommonfinaltasksaftercommandhasbeenfinished

Functionscanbeinjectedintoyourcomponentbyname:

inject:['setIsFullscreen','setIsInProgress','terminate']

Inyourcomponentyouhaveaccesstoacontextandanenvironmentvariable.Theenvironmentvariablecontainsthefollowingproperties(notethatbuilt-incommandshavetotakecarebytheirselvesabouttheterminalsstate):

PropertyDescriptionisExecuting:BooleanIsthecurrentcomponentexecutingisFullscreen:BooleanIstheterminalinfullscreenmodeisInProgress:BooleanIsanycommandactive

Thecontextvariablecontainsthefollowingproperties:

PropertyDescriptioncursor:NumberCopyofcursorpositionatStdinexecuted:SetCopyofexecutedprogramshistory:ArrayCopyofexecutedcommandsparsed:ObjectParsedgetopsargumentspointer:NumberCopyofhistorycommandpointerstdin:StringCopyofStdinBuilt-in

Built-incommandsprovidemorecontrolovertheterminalsbehaviour.Ontheotherside,theyhavetotakecareabouteveryregularcommandstep.Asamatteroffact,regularcommandsarejustcallinghelpermethodsorchangepropertieswhichcouldbealsocalledorchangedbybuilt-incommands.Regularcommandscanbeseenasafacadetobuilt-incommands.

Sincebuilt-incommandscancaptureanycommand,it'snecessarytotakecareofautocompletionandthecommandnotfoundexperience.

Thefirstargumentthatiscalledwithinthebuilt-incommandistheunparsedStdin.It'spossibletouseacustomparseratthisplace.Thesecondargumentistheterminalinstance.YoucanusethecommandNotFoundmethodifnobuilt-inorregularcommandhasbeenfound.

Tofullysimulatearegularcommandcircleabuilt-incommandhastofollowthesesteps:

CallsetIsInProgresswithtruetotellthereisacommandinprogressAddtheprogrammtotheexecutedSetpropertyIncreasethehistorypointerwithsetPointerExecuteactualtaskPushtheStdoutcomponentintothehistorypropertyCallsetIsInProgresswithfalsetotellthereisnocommandinprogressanymoreAutocompletionresolver

Itispossibletoprovideafunctionthatiscalledwhentheuserhitsthe↹key.Thisfunctionneedstotakecareoftheautocompletionexperienceandshouldmakeusageofpropertieslikehistoryandstdin.Thefollowingshowsapossible,simpleautocompletionfunction:

this.autocompletionResolver=()=>{//Makesureonlyprogramsareautocompleted.Seebelowforversionwithoptionsconstcommand=this.stdin.split('')if(command.length>1){return}constautocompleteableProgram=command[0]//Collectallautocompletioncandidatesletcandidates=[]constprograms=[...Object.keys(this.commands)].sort()programs.forEach(program=>{if(program.startsWith(autocompleteableProgram)){candidates.push(program)}})//Autocompletionresolvedintomultipleresultsif(this.stdin!==''&&candidates.length>1){this.history.push({//Buildtableprogrammaticallyrender:createElement=>{constcolumns=candidates.length<5?candidates.length:4constrows=candidates.length<5?1:Math.ceil(candidates.length/columns)letindex=0lettable=[]for(leti=0;i<rows;i++){letrow=[]for(letj=0;j<columns;j++){row.push(createElement('td',candidates[index]))index++}table.push(createElement('tr',[row]))}returncreateElement('table',{style:{width:'100%'}},[table])}})}//Autocompletionresolvedintooneresultif(candidates.length===1){this.stdin=candidates[0]}}Advancedversionwithoptionautocompletionthis.autocompletionResolver=()=>{//Preservecursorpositionconstcursor=this.cursor//Reverseconcatenateautocompletableaccordingtocursorletpointer=this.cursorletautocompleteableStdin=''while(this.stdin[pointer-1]!==''&&pointer-1>0){pointer--autocompleteableStdin=`${this.stdin[pointer]}${autocompleteableStdin}`}//Dividebyargumentsconstcommand=this.stdin.split('')//Autocompleteableisprogramif(command.length===1){constautocompleteableProgram=command[0]//Collectallautocompletioncandidatesconstcandidates=[]constprograms=[...Object.keys(this.commands)].sort()programs.forEach(program=>{if(program.startsWith(autocompleteableProgram)){candidates.push(program)}})//Autocompletionresolvedintomultipleresultsif(this.stdin!==''&&candidates.length>1){this.history.push({//Buildtableprogrammaticallyrender:createElement=>{constcolumns=candidates.length<5?candidates.length:4constrows=candidates.length<5?1:Math.ceil(candidates.length/columns)letindex=0consttable=[]for(leti=0;i<rows;i++){constrow=[]for(letj=0;j<columns;j++){row.push(createElement('td',candidates[index]))index++}table.push(createElement('tr',[row]))}returncreateElement('table',{style:{width:'100%'}},[table])}})}//Autocompletionresolvedintooneresultif(candidates.length===1){//MutatingStdinmutatesthecursor,sowe'vetowaittopushittotheendconstunwatch=this.$watch(()=>this.cursor,()=>{this.cursor=cursor+(candidates[0].length-autocompleteableStdin.length+0)unwatch()})this.stdin=candidates[0]}return}//Checkifoptionmightbecompletedalreadyoroptionislasttokensif((this.stdin[cursor]!==''&&this.stdin[cursor]!=='')&&typeofthis.stdin[cursor]!=='undefined'){return}//Gettheexecutableconstprogram=command[0]//Checkifanyautocompleteableexistsif(typeofthis.options.long[program]==='undefined'&&typeofthis.options.short[program]==='undefined'){return}//Autocompleteableislongoptionif(autocompleteableStdin.substring(0,2)==='--'){constcandidates=[]this.options.long[program].forEach(option=>{//Ifonlydashesarepresent,userrequestsalloptionsif(`--${option}`.startsWith(autocompleteableStdin)||autocompleteableStdin==='--'){candidates.push(option)}})//Autocompletionresolvedintooneresultif(candidates.length===1){constautocompleted=`${this.stdin.substring(0,pointer-1)}--${candidates[0]}`constrest=`${this.stdin.substring(this.cursor)}`//MutatingStdinmutatesthecursor,sowe'vetowaittopushittotheendconstunwatch=this.$watch(()=>this.cursor,()=>{this.cursor=cursor+(candidates[0].length-autocompleteableStdin.length+2)unwatch()})this.stdin=`${autocompleted}${rest}`return}//Autocompletionresolvedintomultipleresultif(autocompleteableStdin==='--'||candidates.length>1){this.history.push({//Buildtableprogrammaticallyrender:createElement=>{constcolumns=candidates.length<5?candidates.length:4constrows=candidates.length<5?1:Math.ceil(candidates.length/columns)letindex=0consttable=[]for(leti=0;i<rows;i++){constrow=[]for(letj=0;j<columns;j++){row.push(createElement('td',`--${candidates[index]}`))index++}table.push(createElement('tr'
声明:本文仅代表作者观点,不代表本站立场。如果侵犯到您的合法权益,请联系我们删除侵权资源!如果遇到资源链接失效,请您通过评论或工单的方式通知管理员。未经允许,不得转载,本站所有资源文章禁止商业使用运营!
下载安装【程序员客栈】APP
实时对接需求、及时收发消息、丰富的开放项目需求、随时随地查看项目状态

评论