| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420 |
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- clink.matches = {}
- clink.generators = {}
- clink.prompt = {}
- clink.prompt.filters = {}
- --------------------------------------------------------------------------------
- function clink.compute_lcd(text, list)
- local list_n = #list
- if list_n < 2 then
- return
- end
- -- Find min and max limits
- local max = 100000
- for i = 1, #list, 1 do
- local j = #(list[i])
- if max > j then
- max = j
- end
- end
- -- For each character in the search range...
- local mid = #text
- local lcd = ""
- for i = 1, max, 1 do
- local same = true
- local l = list[1]:sub(i, i)
- local m = l:lower()
- -- Compare character at the index with each other character in the
- -- other matches.
- for j = 2, list_n, 1 do
- local n = list[j]:sub(i, i):lower()
- if m ~= n then
- same = false
- break
- end
- end
- -- If all characters match then use first match's character.
- if same then
- lcd = lcd..l
- else
- -- Otherwise use what the user's typed or if we're past that then
- -- bail out.
- if i <= mid then
- lcd = lcd..text:sub(i, i)
- else
- break
- end
- end
- end
- return lcd
- end
- --------------------------------------------------------------------------------
- function clink.is_single_match(matches)
- if #matches <= 1 then
- return true
- end
- local first = matches[1]:lower()
- for i = 2, #matches, 1 do
- if first ~= matches[i]:lower() then
- return false
- end
- end
- return true
- end
- --------------------------------------------------------------------------------
- function clink.is_point_in_quote(str, i)
- if i > #str then
- i = #str
- end
- local c = 1
- local q = string.byte("\"")
- for j = 1, i do
- if string.byte(str, j) == q then
- c = c * -1
- end
- end
- if c < 0 then
- return true
- end
- return false
- end
- --------------------------------------------------------------------------------
- function clink.adjust_for_separator(buffer, point, first, last)
- local seps = nil
- if clink.get_host_process() == "cmd.exe" then
- seps = "|&"
- end
- if seps then
- -- Find any valid command separators and if found, manipulate the
- -- completion state a little bit.
- local leading = buffer:sub(1, first - 1)
- -- regex is: <sep> <not_seps> <eol>
- local regex = "["..seps.."]([^"..seps.."]*)$"
- local sep_found, _, post_sep = leading:find(regex)
- if sep_found and not clink.is_point_in_quote(leading, sep_found) then
- local delta = #leading - #post_sep
- buffer = buffer:sub(delta + 1)
- first = first - delta
- last = last - delta
- point = point - delta
- if first < 1 then
- first = 1
- end
- end
- end
- return buffer, point, first, last
- end
- --------------------------------------------------------------------------------
- function clink.generate_matches(text, first, last)
- local line_buffer
- local point
- line_buffer, point, first, last = clink.adjust_for_separator(
- rl_state.line_buffer,
- rl_state.point,
- first,
- last
- )
- rl_state.line_buffer = line_buffer
- rl_state.point = point
- clink.matches = {}
- clink.match_display_filter = nil
- for _, generator in ipairs(clink.generators) do
- if generator.f(text, first, last) == true then
- if #clink.matches > 1 then
- -- Catch instances where there's many entries of a single match
- if clink.is_single_match(clink.matches) then
- clink.matches = { clink.matches[1] }
- return true;
- end
- -- First entry in the match list should be the user's input,
- -- modified here to be the lowest common denominator.
- local lcd = clink.compute_lcd(text, clink.matches)
- table.insert(clink.matches, 1, lcd)
- end
- return true
- end
- end
- return false
- end
- --------------------------------------------------------------------------------
- function clink.add_match(match)
- if type(match) == "table" then
- for _, i in ipairs(match) do
- table.insert(clink.matches, i)
- end
- return
- end
- table.insert(clink.matches, match)
- end
- --------------------------------------------------------------------------------
- function clink.register_match_generator(func, priority)
- if priority == nil then
- priority = 999
- end
- table.insert(clink.generators, {f=func, p=priority})
- table.sort(clink.generators, function(a, b) return a["p"] < b["p"] end)
- end
- --------------------------------------------------------------------------------
- function clink.is_match(needle, candidate)
- if needle == nil then
- error("Nil needle value when calling clink.is_match()", 2)
- end
- if clink.lower(candidate:sub(1, #needle)) == clink.lower(needle) then
- return true
- end
- return false
- end
- --------------------------------------------------------------------------------
- function clink.match_count()
- return #clink.matches
- end
- --------------------------------------------------------------------------------
- function clink.set_match(i, value)
- clink.matches[i] = value
- end
- --------------------------------------------------------------------------------
- function clink.get_match(i)
- return clink.matches[i]
- end
- --------------------------------------------------------------------------------
- function clink.match_words(text, words)
- local count = clink.match_count()
- for _, i in ipairs(words) do
- if clink.is_match(text, i) then
- clink.add_match(i)
- end
- end
- return clink.match_count() - count
- end
- --------------------------------------------------------------------------------
- function clink.match_files(pattern, full_path, find_func)
- -- Fill out default values
- if type(find_func) ~= "function" then
- find_func = clink.find_files
- end
- if full_path == nil then
- full_path = true
- end
- if pattern == nil then
- pattern = "*"
- end
- -- Glob files.
- pattern = pattern:gsub("/", "\\")
- local glob = find_func(pattern, true)
- -- Get glob's base.
- local base = ""
- local i = pattern:find("[\\:][^\\:]*$")
- if i and full_path then
- base = pattern:sub(1, i)
- end
- -- Match them.
- local count = clink.match_count()
- for _, i in ipairs(glob) do
- local full = base..i
- clink.add_match(full)
- end
- return clink.match_count() - count
- end
- --------------------------------------------------------------------------------
- function clink.split(str, sep)
- local i = 1
- local ret = {}
- for _, j in function() return str:find(sep, i, true) end do
- table.insert(ret, str:sub(i, j - 1))
- i = j + 1
- end
- table.insert(ret, str:sub(i, j))
- return ret
- end
- --------------------------------------------------------------------------------
- function clink.quote_split(str, ql, qr)
- if not qr then
- qr = ql
- end
- -- First parse in "pre[ql]quote_string[qr]" chunks
- local insert = table.insert
- local i = 1
- local needle = "%b"..ql..qr
- local parts = {}
- for l, r, quote in function() return str:find(needle, i) end do
- -- "pre"
- if l > 1 then
- insert(parts, str:sub(i, l - 1))
- end
- -- "quote_string"
- insert(parts, str:sub(l, r))
- i = r + 1
- end
- -- Second parse what remains as "pre[ql]being_quoted"
- local l = str:find(ql, i, true)
- if l then
- -- "pre"
- if l > 1 then
- insert(parts, str:sub(i, l - 1))
- end
- -- "being_quoted"
- insert(parts, str:sub(l))
- elseif i <= #str then
- -- Finally add whatever remains...
- insert(parts, str:sub(i))
- end
- return parts
- end
- --------------------------------------------------------------------------------
- function clink.prompt.register_filter(filter, priority)
- if priority == nil then
- priority = 999
- end
- table.insert(clink.prompt.filters, {f=filter, p=priority})
- table.sort(clink.prompt.filters, function(a, b) return a["p"] < b["p"] end)
- end
- --------------------------------------------------------------------------------
- function clink.filter_prompt(prompt)
- local function add_ansi_codes(p)
- local c = tonumber(clink.get_setting_int("prompt_colour"))
- if c < 0 then
- return p
- end
- c = c % 16
- --[[
- <4 >=4 %2
- 0 0 0 Black 4 1 -3 Blue 0
- 1 4 3 Red 5 5 0 Magenta 1
- 2 2 0 Green 6 3 -3 Cyan 0
- 3 6 3 Yellow 7 7 0 Gray 1
- --]]
- -- Convert from cmd.exe colour indices to ANSI ones.
- local colour_id = c % 8
- if (colour_id % 2) == 1 then
- if colour_id < 4 then
- c = c + 3
- end
- elseif colour_id >= 4 then
- c = c - 3
- end
- -- Clamp
- if c > 15 then
- c = 15
- end
- -- Build ANSI code
- local code = "\x1b[0;"
- if c > 7 then
- c = c - 8
- code = code.."1;"
- end
- code = code..(c + 30).."m"
- return code..p.."\x1b[0m"
- end
- clink.prompt.value = prompt
- for _, filter in ipairs(clink.prompt.filters) do
- if filter.f() == true then
- return add_ansi_codes(clink.prompt.value)
- end
- end
- return add_ansi_codes(clink.prompt.value)
- end
- -- vim: expandtab
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- clink.arg = {}
- --------------------------------------------------------------------------------
- local parsers = {}
- local is_parser
- local is_sub_parser
- local new_sub_parser
- local parser_go_impl
- local merge_parsers
- local parser_meta_table = {}
- local sub_parser_meta_table = {}
- --------------------------------------------------------------------------------
- function parser_meta_table.__concat(lhs, rhs)
- if not is_parser(rhs) then
- error("Right-handside must be parser.", 2)
- end
- local t = type(lhs)
- if t == "table" then
- local ret = {}
- for _, i in ipairs(lhs) do
- table.insert(ret, i .. rhs)
- end
- return ret
- elseif t ~= "string" then
- error("Left-handside must be a string or a table.", 2)
- end
- return new_sub_parser(lhs, rhs)
- end
- --------------------------------------------------------------------------------
- local function unfold_table(source, target)
- for _, i in ipairs(source) do
- if type(i) == "table" and getmetatable(i) == nil then
- unfold_table(i, target)
- else
- table.insert(target, i)
- end
- end
- end
- --------------------------------------------------------------------------------
- local function parser_is_flag(parser, part)
- if part == nil then
- return false
- end
- local prefix = part:sub(1, 1)
- return prefix == "-" or prefix == "/"
- end
- --------------------------------------------------------------------------------
- local function parser_add_arguments(parser, ...)
- for _, i in ipairs({...}) do
- -- Check all arguments are tables.
- if type(i) ~= "table" then
- error("All arguments to add_arguments() must be tables.", 2)
- end
- -- Only parsers are allowed to be specified without being wrapped in a
- -- containing table.
- if getmetatable(i) ~= nil then
- if is_parser(i) then
- table.insert(parser.arguments, i)
- else
- error("Tables can't have meta-tables.", 2)
- end
- else
- -- Expand out nested tables and insert into object's arguments table.
- local arguments = {}
- unfold_table(i, arguments)
- table.insert(parser.arguments, arguments)
- end
- end
- return parser
- end
- --------------------------------------------------------------------------------
- local function parser_set_arguments(parser, ...)
- parser.arguments = {}
- return parser:add_arguments(...)
- end
- --------------------------------------------------------------------------------
- local function parser_add_flags(parser, ...)
- local flags = {}
- unfold_table({...}, flags)
- -- Validate the specified flags.
- for _, i in ipairs(flags) do
- if is_sub_parser(i) then
- i = i.key
- end
- -- Check all flags are strings.
- if type(i) ~= "string" then
- error("All parser flags must be strings. Found "..type(i), 2)
- end
- -- Check all flags start with a - or a /
- if not parser:is_flag(i) then
- error("Flags must begin with a '-' or a '/'", 2)
- end
- end
- -- Append flags to parser's existing table of flags.
- for _, i in ipairs(flags) do
- table.insert(parser.flags, i)
- end
- return parser
- end
- --------------------------------------------------------------------------------
- local function parser_set_flags(parser, ...)
- parser.flags = {}
- return parser:add_flags(...)
- end
- --------------------------------------------------------------------------------
- local function parser_flatten_argument(parser, index, func_thunk)
- -- Sanity check the 'index' param to make sure it's valid.
- if type(index) == "number" then
- if index <= 0 or index > #parser.arguments then
- return parser.use_file_matching
- end
- end
- -- index == nil is a special case that returns the parser's flags
- local opts = {}
- local arg_opts
- if index == nil then
- arg_opts = parser.flags
- else
- arg_opts = parser.arguments[index]
- end
- -- Convert each argument option into a string and collect them in a table.
- for _, i in ipairs(arg_opts) do
- if is_sub_parser(i) then
- table.insert(opts, i.key)
- else
- local t = type(i)
- if t == "function" then
- local results = func_thunk(i)
- local t = type(results)
- if not results then
- return parser.use_file_matching
- elseif t == "boolean" then
- return (results and parser.use_file_matching)
- elseif t == "table" then
- for _, j in ipairs(results) do
- table.insert(opts, j)
- end
- end
- elseif t == "string" or t == "number" then
- table.insert(opts, tostring(i))
- end
- end
- end
- return opts
- end
- --------------------------------------------------------------------------------
- local function parser_go_args(parser, state)
- local exhausted_args = false
- local exhausted_parts = false
- local part = state.parts[state.part_index]
- local arg_index = state.arg_index
- local arg_opts = parser.arguments[arg_index]
- local arg_count = #parser.arguments
- -- Is the next argument a parser? Parse control directly on to it.
- if is_parser(arg_opts) then
- state.arg_index = 1
- return parser_go_impl(arg_opts, state)
- end
- -- Advance parts state.
- state.part_index = state.part_index + 1
- if state.part_index > #state.parts then
- exhausted_parts = true
- end
- -- Advance argument state.
- state.arg_index = arg_index + 1
- if arg_index > arg_count then
- exhausted_args = true
- end
- -- We've exhausted all available arguments. We either loop or we're done.
- if parser.loop_point > 0 and state.arg_index > arg_count then
- state.arg_index = parser.loop_point
- if state.arg_index > arg_count then
- state.arg_index = arg_count
- end
- end
- -- Is there some state to process?
- if not exhausted_parts and not exhausted_args then
- local exact = false
- for _, arg_opt in ipairs(arg_opts) do
- -- Is the argument a key to a sub-parser? If so then hand control
- -- off to it.
- if is_sub_parser(arg_opt) then
- if arg_opt.key == part then
- state.arg_index = 1
- return parser_go_impl(arg_opt.parser, state)
- end
- end
- -- Check so see if the part has an exact match in the argument. Note
- -- that only string-type options are considered.
- if type(arg_opt) == "string" then
- exact = exact or arg_opt == part
- else
- exact = true
- end
- end
- -- If the parser's required to be precise then check here.
- if parser.precise and not exact then
- exhausted_args = true
- else
- return nil
- end
- end
- -- If we've no more arguments to traverse but there's still parts remaining
- -- then we start skipping arguments but keep going so that flags still get
- -- parsed (as flags have no position).
- if exhausted_args then
- state.part_index = state.part_index - 1
- if not exhausted_parts then
- if state.depth <= 1 then
- state.skip_args = true
- return
- end
- return parser.use_file_matching
- end
- end
- -- Now we've an index into the parser's arguments that matches the line
- -- state. Flatten it.
- local func_thunk = function(func)
- return func(part)
- end
- return parser:flatten_argument(arg_index, func_thunk)
- end
- --------------------------------------------------------------------------------
- local function parser_go_flags(parser, state)
- local part = state.parts[state.part_index]
- -- Advance parts state.
- state.part_index = state.part_index + 1
- if state.part_index > #state.parts then
- return parser:flatten_argument()
- end
- for _, arg_opt in ipairs(parser.flags) do
- if is_sub_parser(arg_opt) then
- if arg_opt.key == part then
- local arg_index_cache = state.arg_index
- local skip_args_cache = state.skip_args
- state.arg_index = 1
- state.skip_args = false
- state.depth = state.depth + 1
- local ret = parser_go_impl(arg_opt.parser, state)
- if type(ret) == "table" then
- return ret
- end
- state.depth = state.depth - 1
- state.skip_args = skip_args_cache
- state.arg_index = arg_index_cache
- end
- end
- end
- end
- --------------------------------------------------------------------------------
- function parser_go_impl(parser, state)
- local has_flags = #parser.flags > 0
- while state.part_index <= #state.parts do
- local part = state.parts[state.part_index]
- local dispatch_func
- if has_flags and parser:is_flag(part) then
- dispatch_func = parser_go_flags
- elseif not state.skip_args then
- dispatch_func = parser_go_args
- end
- if dispatch_func ~= nil then
- local ret = dispatch_func(parser, state)
- if ret ~= nil then
- return ret
- end
- else
- state.part_index = state.part_index + 1
- end
- end
- return parser.use_file_matching
- end
- --------------------------------------------------------------------------------
- local function parser_go(parser, parts)
- -- Validate 'parts'.
- if type(parts) ~= "table" then
- error("'Parts' param must be a table of strings ("..type(parts)..").", 2)
- else
- if #parts == 0 then
- part = { "" }
- end
- for i, j in ipairs(parts) do
- local t = type(parts[i])
- if t ~= "string" then
- error("'Parts' table can only contain strings; "..j.."="..t, 2)
- end
- end
- end
- local state = {
- arg_index = 1,
- part_index = 1,
- parts = parts,
- skip_args = false,
- depth = 1,
- }
- return parser_go_impl(parser, state)
- end
- --------------------------------------------------------------------------------
- local function parser_dump(parser, depth)
- if depth == nil then
- depth = 0
- end
- function prt(depth, index, text)
- local indent = string.sub(" ", 1, depth)
- text = tostring(text)
- print(indent..depth.."."..index.." - "..text)
- end
- -- Print arguments
- local i = 0
- for _, arg_opts in ipairs(parser.arguments) do
- for _, arg_opt in ipairs(arg_opts) do
- if is_sub_parser(arg_opt) then
- prt(depth, i, arg_opt.key)
- arg_opt.parser:dump(depth + 1)
- else
- prt(depth, i, arg_opt)
- end
- end
- i = i + 1
- end
- -- Print flags
- for _, flag in ipairs(parser.flags) do
- prt(depth, "F", flag)
- end
- end
- --------------------------------------------------------------------------------
- function parser_be_precise(parser)
- parser.precise = true
- return parser
- end
- --------------------------------------------------------------------------------
- function is_parser(p)
- return type(p) == "table" and getmetatable(p) == parser_meta_table
- end
- --------------------------------------------------------------------------------
- function is_sub_parser(sp)
- return type(sp) == "table" and getmetatable(sp) == sub_parser_meta_table
- end
- --------------------------------------------------------------------------------
- local function get_sub_parser(argument, str)
- for _, arg in ipairs(argument) do
- if is_sub_parser(arg) then
- if arg.key == str then
- return arg.parser
- end
- end
- end
- end
- --------------------------------------------------------------------------------
- function new_sub_parser(key, parser)
- local sub_parser = {}
- sub_parser.key = key
- sub_parser.parser = parser
- setmetatable(sub_parser, sub_parser_meta_table)
- return sub_parser
- end
- --------------------------------------------------------------------------------
- local function parser_disable_file_matching(parser)
- parser.use_file_matching = false
- return parser
- end
- --------------------------------------------------------------------------------
- local function parser_loop(parser, loop_point)
- if loop_point == nil or type(loop_point) ~= "number" or loop_point < 1 then
- loop_point = 1
- end
- parser.loop_point = loop_point
- return parser
- end
- --------------------------------------------------------------------------------
- local function parser_initialise(parser, ...)
- for _, word in ipairs({...}) do
- local t = type(word)
- if t == "string" then
- parser:add_flags(word)
- elseif t == "table" then
- if is_sub_parser(word) and parser_is_flag(nil, word.key) then
- parser:add_flags(word)
- else
- parser:add_arguments(word)
- end
- else
- error("Additional arguments to new_parser() must be tables or strings", 2)
- end
- end
- end
- --------------------------------------------------------------------------------
- function clink.arg.new_parser(...)
- local parser = {}
- -- Methods
- parser.set_flags = parser_set_flags
- parser.add_flags = parser_add_flags
- parser.set_arguments = parser_set_arguments
- parser.add_arguments = parser_add_arguments
- parser.dump = parser_dump
- parser.go = parser_go
- parser.flatten_argument = parser_flatten_argument
- parser.be_precise = parser_be_precise
- parser.disable_file_matching = parser_disable_file_matching
- parser.loop = parser_loop
- parser.is_flag = parser_is_flag
- -- Members.
- parser.flags = {}
- parser.arguments = {}
- parser.precise = false
- parser.use_file_matching = true
- parser.loop_point = 0
- setmetatable(parser, parser_meta_table)
- -- If any arguments are provided treat them as parser's arguments or flags
- if ... then
- success, msg = pcall(parser_initialise, parser, ...)
- if not success then
- error(msg, 2)
- end
- end
- return parser
- end
- --------------------------------------------------------------------------------
- function merge_parsers(lhs, rhs)
- -- Merging parsers is not a trivial matter and this implementation is far
- -- from correct. It is however sufficient for the majority of cases.
- -- Merge flags.
- for _, rflag in ipairs(rhs.flags) do
- table.insert(lhs.flags, rflag)
- end
- -- Remove (and save value of) the first argument in RHS.
- local rhs_arg_1 = table.remove(rhs.arguments, 1)
- if rhs_arg_1 == nil then
- return
- end
- -- Get reference to the LHS's first argument table (creating it if needed).
- local lhs_arg_1 = lhs.arguments[1]
- if lhs_arg_1 == nil then
- lhs_arg_1 = {}
- table.insert(lhs.arguments, lhs_arg_1)
- end
- -- Link RHS to LHS through sub-parsers.
- for _, rarg in ipairs(rhs_arg_1) do
- local child
- -- Split sub parser
- if is_sub_parser(rarg) then
- child = rarg.parser
- rarg = rarg.key
- else
- child = rhs
- end
- -- If LHS's first argument has rarg in it which links to a sub-parser
- -- then we need to recursively merge them.
- local lhs_sub_parser = get_sub_parser(lhs_arg_1, rarg)
- if lhs_sub_parser then
- merge_parsers(lhs_sub_parser, child)
- else
- local to_add = rarg
- if type(rarg) ~= "function" then
- to_add = rarg .. child
- end
- table.insert(lhs_arg_1, to_add)
- end
- end
- end
- --------------------------------------------------------------------------------
- function clink.arg.register_parser(cmd, parser)
- if not is_parser(parser) then
- local p = clink.arg.new_parser()
- p:set_arguments({ parser })
- parser = p
- end
- cmd = cmd:lower()
- local prev = parsers[cmd]
- if prev ~= nil then
- merge_parsers(prev, parser)
- else
- parsers[cmd] = parser
- end
- end
- --------------------------------------------------------------------------------
- local function argument_match_generator(text, first, last)
- local leading = rl_state.line_buffer:sub(1, first - 1):lower()
- -- Extract the command.
- local cmd_l, cmd_r
- if leading:find("^%s*\"") then
- -- Command appears to be surround by quotes.
- cmd_l, cmd_r = leading:find("%b\"\"")
- if cmd_l and cmd_r then
- cmd_l = cmd_l + 1
- cmd_r = cmd_r - 1
- end
- else
- -- No quotes so the first, longest, non-whitespace word is extracted.
- cmd_l, cmd_r = leading:find("[^%s]+")
- end
- if not cmd_l or not cmd_r then
- return false
- end
- local regex = "[\\/:]*([^\\/:.]+)(%.*[%l]*)%s*$"
- local _, _, cmd, ext = leading:sub(cmd_l, cmd_r):lower():find(regex)
- -- Check to make sure the extension extracted is in pathext.
- if ext and ext ~= "" then
- if not clink.get_env("pathext"):lower():match(ext.."[;$]", 1, true) then
- return false
- end
- end
-
- -- Find a registered parser.
- local parser = parsers[cmd]
- if parser == nil then
- return false
- end
- -- Split the command line into parts.
- local str = rl_state.line_buffer:sub(cmd_r + 2, last)
- local parts = {}
- for _, sub_str in ipairs(clink.quote_split(str, "\"")) do
- -- Quoted strings still have their quotes. Look for those type of
- -- strings, strip the quotes and add it completely.
- if sub_str:sub(1, 1) == "\"" then
- local l, r = sub_str:find("\"[^\"]+")
- if l then
- local part = sub_str:sub(l + 1, r)
- table.insert(parts, part)
- end
- else
- -- Extract non-whitespace parts.
- for _, r, part in function () return sub_str:find("^%s*([^%s]+)") end do
- table.insert(parts, part)
- sub_str = sub_str:sub(r + 1)
- end
- end
- end
- -- If 'text' is empty then add it as a part as it would have been skipped
- -- by the split loop above.
- if text == "" then
- table.insert(parts, text)
- end
- -- Extend rl_state with match generation state; text, first, and last.
- rl_state.text = text
- rl_state.first = first
- rl_state.last = last
- -- Call the parser.
- local needle = parts[#parts]
- local ret = parser:go(parts)
- if type(ret) ~= "table" then
- return not ret
- end
- -- Iterate through the matches the parser returned and collect matches.
- for _, match in ipairs(ret) do
- if clink.is_match(needle, match) then
- clink.add_match(match)
- end
- end
- return true
- end
- --------------------------------------------------------------------------------
- clink.register_match_generator(argument_match_generator, 25)
- -- vim: expandtab
- --{{{ history
- --15/03/06 DCN Created based on RemDebug
- --28/04/06 DCN Update for Lua 5.1
- --01/06/06 DCN Fix command argument parsing
- -- Add step/over N facility
- -- Add trace lines facility
- --05/06/06 DCN Add trace call/return facility
- --06/06/06 DCN Make it behave when stepping through the creation of a coroutine
- --06/06/06 DCN Integrate the simple debugger into the main one
- --07/06/06 DCN Provide facility to step into coroutines
- --13/06/06 DCN Fix bug that caused the function environment to get corrupted with the global one
- --14/06/06 DCN Allow 'sloppy' file names when setting breakpoints
- --04/08/06 DCN Allow for no space after command name
- --11/08/06 DCN Use io.write not print
- --30/08/06 DCN Allow access to array elements in 'dump'
- --10/10/06 DCN Default to breakfile for all commands that require a filename and give '-'
- --06/12/06 DCN Allow for punctuation characters in DUMP variable names
- --03/01/07 DCN Add pause on/off facility
- --19/06/07 DCN Allow for duff commands being typed in the debugger (thanks to [email protected])
- -- Allow for case sensitive file systems (thanks to [email protected])
- --04/08/09 DCN Add optional line count param to pause
- --05/08/09 DCN Reset the debug hook in Pause() even if we think we're started
- --30/09/09 DCN Re-jig to not use co-routines (makes debugging co-routines awkward)
- --01/10/09 DCN Add ability to break on reaching any line in a file
- --24/07/13 TWW Added code for emulating setfenv/getfenv in Lua 5.2 as per
- -- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
- --25/07/13 TWW Copied Alex Parrill's fix for errors when tracing back across a C frame
- -- (https://github.com/ColonelThirtyTwo/clidebugger, 26/01/12)
- --25/07/13 DCN Allow for windows and unix file name conventions in has_breakpoint
- --26/07/13 DCN Allow for \ being interpreted as an escape inside a [] pattern in 5.2
- --}}}
- --{{{ description
- --A simple command line debug system for Lua written by Dave Nichols of
- --Match-IT Limited. Its public domain software. Do with it as you wish.
- --This debugger was inspired by:
- -- RemDebug 1.0 Beta
- -- Copyright Kepler Project 2005 (http://www.keplerproject.org/remdebug)
- --Usage:
- -- require('debugger') --load the debug library
- -- pause(message) --start/resume a debug session
- --An assert() failure will also invoke the debugger.
- --}}}
- local IsWindows = string.find(string.lower(os.getenv('OS') or ''),'^windows')
- local coro_debugger
- local events = { BREAK = 1, WATCH = 2, STEP = 3, SET = 4 }
- local breakpoints = {}
- local watches = {}
- local step_into = false
- local step_over = false
- local step_lines = 0
- local step_level = {main=0}
- local stack_level = {main=0}
- local trace_level = {main=0}
- local trace_calls = false
- local trace_returns = false
- local trace_lines = false
- local ret_file, ret_line, ret_name
- local current_thread = 'main'
- local started = false
- local pause_off = false
- local _g = _G
- local cocreate, cowrap = coroutine.create, coroutine.wrap
- local pausemsg = 'pause'
- local aliases = {
- p = "over",
- t = "step",
- q = "exit",
- g = "run",
- dv = "dump",
- dt = "locs",
- k = "trace",
- bp = "setb",
- bc = "delb",
- bl = "listb",
- pt = "out",
- }
- --{{{ make Lua 5.2 compatible
- if not setfenv then -- Lua 5.2
- --[[
- As far as I can see, the only missing detail of these functions (except
- for occasional bugs) to achieve 100% compatibility is the case of
- 'getfenv' over a function that does not have an _ENV variable (that is,
- it uses no globals).
- We could use a weak table to keep the environments of these functions
- when set by setfenv, but that still misses the case of a function
- without _ENV that was not subjected to setfenv.
- -- Roberto
- ]]--
- setfenv = setfenv or function(f, t)
- f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
- local name
- local up = 0
- repeat
- up = up + 1
- name = debug.getupvalue(f, up)
- until name == '_ENV' or name == nil
- if name then
- debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue
- debug.setupvalue(f, up, t)
- end
- end
- getfenv = getfenv or function(f)
- f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
- local name, val
- local up = 0
- repeat
- up = up + 1
- name, val = debug.getupvalue(f, up)
- until name == '_ENV' or name == nil
- return val
- end
- unpack = table.unpack
- end
- --}}}
- --{{{ local hints -- command help
- --The format in here is name=summary|description
- local hints = {
- pause = [[
- pause(msg[,lines][,force]) -- start/resume a debugger session|
- This can only be used in your code or from the console as a means to
- start/resume a debug session.
- If msg is given that is shown when the session starts/resumes. Useful to
- give a context if you've instrumented your code with pause() statements.
- If lines is given, the script pauses after that many lines, else it pauses
- immediately.
- If force is true, the pause function is honoured even if poff has been used.
- This is useful when in an interactive console session to regain debugger
- control.
- ]],
- poff = [[
- poff -- turn off pause() command|
- This causes all pause() commands to be ignored. This is useful if you have
- instrumented your code in a busy loop and want to continue normal execution
- with no further interruption.
- ]],
- pon = [[
- pon -- turn on pause() command|
- This re-instates honouring the pause() commands you may have instrumented
- your code with.
- ]],
- setb = [[
- setb [line file] -- set a breakpoint to line/file|, line 0 means 'any'
- If file is omitted or is "-" the breakpoint is set at the file for the
- currently set level (see "set"). Execution pauses when this line is about
- to be executed and the debugger session is re-activated.
- The file can be given as the fully qualified name, partially qualified or
- just the file name. E.g. if file is set as "myfile.lua", then whenever
- execution reaches any file that ends with "myfile.lua" it will pause. If
- no extension is given, any extension will do.
- If the line is given as 0, then reaching any line in the file will do.
- ]],
- delb = [[
- delb [line file] -- removes a breakpoint|
- If file is omitted or is "-" the breakpoint is removed for the file of the
- currently set level (see "set").
- ]],
- delallb = [[
- delallb -- removes all breakpoints|
- ]],
- setw = [[
- setw <exp> -- adds a new watch expression|
- The expression is evaluated before each line is executed. If the expression
- yields true then execution is paused and the debugger session re-activated.
- The expression is executed in the context of the line about to be executed.
- ]],
- delw = [[
- delw <index> -- removes the watch expression at index|
- The index is that returned when the watch expression was set by setw.
- ]],
- delallw = [[
- delallw -- removes all watch expressions|
- ]],
- run = [[
- run -- run until next breakpoint or watch expression|
- ]],
- step = [[
- step [N] -- run next N lines, stepping into function calls|
- If N is omitted, use 1.
- ]],
- over = [[
- over [N] -- run next N lines, stepping over function calls|
- If N is omitted, use 1.
- ]],
- out = [[
- out [N] -- run lines until stepped out of N functions|
- If N is omitted, use 1.
- If you are inside a function, using "out 1" will run until you return
- from that function to the caller.
- ]],
- gotoo = [[
- gotoo [line file] -- step to line in file|
- This is equivalent to 'setb line file', followed by 'run', followed
- by 'delb line file'.
- ]],
- listb = [[
- listb -- lists breakpoints|
- ]],
- listw = [[
- listw -- lists watch expressions|
- ]],
- set = [[
- set [level] -- set context to stack level, omitted=show|
- If level is omitted it just prints the current level set.
- This sets the current context to the level given. This affects the
- context used for several other functions (e.g. vars). The possible
- levels are those shown by trace.
- ]],
- vars = [[
- vars [depth] -- list context locals to depth, omitted=1|
- If depth is omitted then uses 1.
- Use a depth of 0 for the maximum.
- Lists all non-nil local variables and all non-nil upvalues in the
- currently set context. For variables that are tables, lists all fields
- to the given depth.
- ]],
- fenv = [[
- fenv [depth] -- list context function env to depth, omitted=1|
- If depth is omitted then uses 1.
- Use a depth of 0 for the maximum.
- Lists all function environment variables in the currently set context.
- For variables that are tables, lists all fields to the given depth.
- ]],
- glob = [[
- glob [depth] -- list globals to depth, omitted=1|
- If depth is omitted then uses 1.
- Use a depth of 0 for the maximum.
- Lists all global variables.
- For variables that are tables, lists all fields to the given depth.
- ]],
- ups = [[
- ups -- list all the upvalue names|
- These names will also be in the "vars" list unless their value is nil.
- This provides a means to identify which vars are upvalues and which are
- locals. If a name is both an upvalue and a local, the local value takes
- precedance.
- ]],
- locs = [[
- locs -- list all the locals names|
- These names will also be in the "vars" list unless their value is nil.
- This provides a means to identify which vars are upvalues and which are
- locals. If a name is both an upvalue and a local, the local value takes
- precedance.
- ]],
- dump = [[
- dump <var> [depth] -- dump all fields of variable to depth|
- If depth is omitted then uses 1.
- Use a depth of 0 for the maximum.
- Prints the value of <var> in the currently set context level. If <var>
- is a table, lists all fields to the given depth. <var> can be just a
- name, or name.field or name.# to any depth, e.g. t.1.f accesses field
- 'f' in array element 1 in table 't'.
- Can also be called from a script as dump(var,depth).
- ]],
- tron = [[
- tron [crl] -- turn trace on for (c)alls, (r)etuns, (l)lines|
- If no parameter is given then tracing is turned off.
- When tracing is turned on a line is printed to the console for each
- debug 'event' selected. c=function calls, r=function returns, l=lines.
- ]],
- trace = [[
- trace -- dumps a stack trace|
- Format is [level] = file,line,name
- The level is a candidate for use by the 'set' command.
- ]],
- info = [[
- info -- dumps the complete debug info captured|
- Only useful as a diagnostic aid for the debugger itself. This information
- can be HUGE as it dumps all variables to the maximum depth, so be careful.
- ]],
- show = [[
- show line file X Y -- show X lines before and Y after line in file|
- If line is omitted or is '-' then the current set context line is used.
- If file is omitted or is '-' then the current set context file is used.
- If file is not fully qualified and cannot be opened as specified, then
- a search for the file in the package[path] is performed using the usual
- "require" searching rules. If no file extension is given, .lua is used.
- Prints the lines from the source file around the given line.
- ]],
- exit = [[
- exit -- exits debugger, re-start it using pause()|
- ]],
- help = [[
- help [command] -- show this list or help for command|
- ]],
- ["<statement>"] = [[
- <statement> -- execute a statement in the current context|
- The statement can be anything that is legal in the context, including
- assignments. Such assignments affect the context and will be in force
- immediately. Any results returned are printed. Use '=' as a short-hand
- for 'return', e.g. "=func(arg)" will call 'func' with 'arg' and print
- the results, and "=var" will just print the value of 'var'.
- ]],
- what = [[
- what <func> -- show where <func> is defined (if known)|
- ]],
- }
- --}}}
- --{{{ local function getinfo(level,field)
- --like debug.getinfo but copes with no activation record at the given level
- --and knows how to get 'field'. 'field' can be the name of any of the
- --activation record fields or any of the 'what' names or nil for everything.
- --only valid when using the stack level to get info, not a function name.
- local function getinfo(level,field)
- level = level + 1 --to get to the same relative level as the caller
- if not field then return debug.getinfo(level) end
- local what
- if field == 'name' or field == 'namewhat' then
- what = 'n'
- elseif field == 'what' or field == 'source' or field == 'linedefined' or field == 'lastlinedefined' or field == 'short_src' then
- what = 'S'
- elseif field == 'currentline' then
- what = 'l'
- elseif field == 'nups' then
- what = 'u'
- elseif field == 'func' then
- what = 'f'
- else
- return debug.getinfo(level,field)
- end
- local ar = debug.getinfo(level,what)
- if ar then return ar[field] else return nil end
- end
- --}}}
- --{{{ local function indented( level, ... )
- local function indented( level, ... )
- io.write( string.rep(' ',level), table.concat({...}), '\n' )
- end
- --}}}
- --{{{ local function dumpval( level, name, value, limit )
- local dumpvisited
- local function dumpval( level, name, value, limit )
- local index
- if type(name) == 'number' then
- index = string.format('[%d] = ',name)
- elseif type(name) == 'string'
- and (name == '__VARSLEVEL__' or name == '__ENVIRONMENT__' or name == '__GLOBALS__' or name == '__UPVALUES__' or name == '__LOCALS__') then
- --ignore these, they are debugger generated
- return
- elseif type(name) == 'string' and string.find(name,'^[_%a][_.%w]*$') then
- index = name ..' = '
- else
- index = string.format('[%q] = ',tostring(name))
- end
- if type(value) == 'table' then
- if dumpvisited[value] then
- indented( level, index, string.format('ref%q;',dumpvisited[value]) )
- else
- dumpvisited[value] = tostring(value)
- if (limit or 0) > 0 and level+1 >= limit then
- indented( level, index, dumpvisited[value] )
- else
- indented( level, index, '{ -- ', dumpvisited[value] )
- for n,v in pairs(value) do
- dumpval( level+1, n, v, limit )
- end
- indented( level, '};' )
- end
- end
- else
- if type(value) == 'string' then
- if string.len(value) > 40 then
- indented( level, index, '[[', value, ']];' )
- else
- indented( level, index, string.format('%q',value), ';' )
- end
- else
- indented( level, index, tostring(value), ';' )
- end
- end
- end
- --}}}
- --{{{ local function dumpvar( value, limit, name )
- local function dumpvar( value, limit, name )
- dumpvisited = {}
- dumpval( 0, name or tostring(value), value, limit )
- end
- --}}}
- --{{{ local function show(file,line,before,after)
- --show +/-N lines of a file around line M
- local function show(file,line,before,after)
- line = tonumber(line or 1)
- before = tonumber(before or 10)
- after = tonumber(after or before)
- if not string.find(file,'%.') then file = file..'.lua' end
- local f = io.open(file,'r')
- if not f then
- --{{{ try to find the file in the path
-
- --
- -- looks for a file in the package path
- --
- local path = package.path or LUA_PATH or ''
- for c in string.gmatch (path, "[^;]+") do
- local c = string.gsub (c, "%?%.lua", file)
- f = io.open (c,'r')
- if f then
- break
- end
- end
-
- --}}}
- if not f then
- io.write('Cannot find '..file..'\n')
- return
- end
- end
- local i = 0
- for l in f:lines() do
- i = i + 1
- if i >= (line-before) then
- if i > (line+after) then break end
- if i == line then
- io.write(i..'***\t'..l..'\n')
- else
- io.write(i..'\t'..l..'\n')
- end
- end
- end
- f:close()
- end
- --}}}
- --{{{ local function tracestack(l)
- local function gi( i )
- return function() i=i+1 return debug.getinfo(i),i end
- end
- local function gl( level, j )
- return function() j=j+1 return debug.getlocal( level, j ) end
- end
- local function gu( func, k )
- return function() k=k+1 return debug.getupvalue( func, k ) end
- end
- local traceinfo
- local function tracestack(l)
- local l = l + 1 --NB: +1 to get level relative to caller
- traceinfo = {}
- traceinfo.pausemsg = pausemsg
- for ar,i in gi(l) do
- table.insert( traceinfo, ar )
- if ar.what ~= 'C' then
- local names = {}
- local values = {}
- for n,v in gl(i,0) do
- if string.sub(n,1,1) ~= '(' then --ignore internal control variables
- table.insert( names, n )
- table.insert( values, v )
- end
- end
- if #names > 0 then
- ar.lnames = names
- ar.lvalues = values
- end
- end
- if ar.func then
- local names = {}
- local values = {}
- for n,v in gu(ar.func,0) do
- if string.sub(n,1,1) ~= '(' then --ignore internal control variables
- table.insert( names, n )
- table.insert( values, v )
- end
- end
- if #names > 0 then
- ar.unames = names
- ar.uvalues = values
- end
- end
- end
- end
- --}}}
- --{{{ local function trace()
- local function trace(set)
- local mark
- for level,ar in ipairs(traceinfo) do
- if level == set then
- mark = '***'
- else
- mark = ''
- end
- io.write('['..level..']'..mark..'\t'..(ar.name or ar.what)..' in '..ar.short_src..':'..ar.currentline..'\n')
- end
- end
- --}}}
- --{{{ local function info()
- local function info() dumpvar( traceinfo, 0, 'traceinfo' ) end
- --}}}
- --{{{ local function set_breakpoint(file, line, once)
- local function set_breakpoint(file, line, once)
- if not breakpoints[line] then
- breakpoints[line] = {}
- end
- if once then
- breakpoints[line][file] = 1
- else
- breakpoints[line][file] = true
- end
- end
- --}}}
- --{{{ local function remove_breakpoint(file, line)
- local function remove_breakpoint(file, line)
- if breakpoints[line] then
- breakpoints[line][file] = nil
- end
- end
- --}}}
- --{{{ local function has_breakpoint(file, line)
- --allow for 'sloppy' file names
- --search for file and all variations walking up its directory hierachy
- --ditto for the file with no extension
- --a breakpoint can be permenant or once only, if once only its removed
- --after detection here, these are used for temporary breakpoints in the
- --debugger loop when executing the 'gotoo' command
- --a breakpoint on line 0 of a file means any line in that file
- local function has_breakpoint(file, line)
- local isLine = breakpoints[line]
- local isZero = breakpoints[0]
- if not isLine and not isZero then return false end
- local noext = string.gsub(file,"(%..-)$",'',1)
- if noext == file then noext = nil end
- while file do
- if isLine and isLine[file] then
- if isLine[file] == 1 then isLine[file] = nil end
- return true
- end
- if isZero and isZero[file] then
- if isZero[file] == 1 then isZero[file] = nil end
- return true
- end
- if IsWindows then
- file = string.match(file,"[:/\\](.+)$")
- else
- file = string.match(file,"[:/](.+)$")
- end
- end
- while noext do
- if isLine and isLine[noext] then
- if isLine[noext] == 1 then isLine[noext] = nil end
- return true
- end
- if isZero and isZero[noext] then
- if isZero[noext] == 1 then isZero[noext] = nil end
- return true
- end
- if IsWindows then
- noext = string.match(noext,"[:/\\](.+)$")
- else
- noext = string.match(noext,"[:/](.+)$")
- end
- end
- return false
- end
- --}}}
- --{{{ local function capture_vars(ref,level,line)
- local function capture_vars(ref,level,line)
- --get vars, file and line for the given level relative to debug_hook offset by ref
- local lvl = ref + level --NB: This includes an offset of +1 for the call to here
- --{{{ capture variables
-
- local ar = debug.getinfo(lvl, "f")
- if not ar then return {},'?',0 end
-
- local vars = {__UPVALUES__={}, __LOCALS__={}}
- local i
-
- local func = ar.func
- if func then
- i = 1
- while true do
- local name, value = debug.getupvalue(func, i)
- if not name then break end
- if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- vars[name] = value
- vars.__UPVALUES__[i] = name
- end
- i = i + 1
- end
- vars.__ENVIRONMENT__ = getfenv(func)
- end
-
- vars.__GLOBALS__ = getfenv(0)
-
- i = 1
- while true do
- local name, value = debug.getlocal(lvl, i)
- if not name then break end
- if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- vars[name] = value
- vars.__LOCALS__[i] = name
- end
- i = i + 1
- end
-
- vars.__VARSLEVEL__ = level
-
- if func then
- --NB: Do not do this until finished filling the vars table
- setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) })
- end
-
- --NB: Do not read or write the vars table anymore else the metatable functions will get invoked!
-
- --}}}
- local file = getinfo(lvl, "source")
- if string.find(file, "@") == 1 then
- file = string.sub(file, 2)
- end
- if IsWindows then file = string.lower(file) end
- if not line then
- line = getinfo(lvl, "currentline")
- end
- return vars,file,line
- end
- --}}}
- --{{{ local function restore_vars(ref,vars)
- local function restore_vars(ref,vars)
- if type(vars) ~= 'table' then return end
- local level = vars.__VARSLEVEL__ --NB: This level is relative to debug_hook offset by ref
- if not level then return end
- level = level + ref --NB: This includes an offset of +1 for the call to here
- local i
- local written_vars = {}
- i = 1
- while true do
- local name, value = debug.getlocal(level, i)
- if not name then break end
- if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- debug.setlocal(level, i, vars[name])
- written_vars[name] = true
- end
- i = i + 1
- end
- local ar = debug.getinfo(level, "f")
- if not ar then return end
- local func = ar.func
- if func then
- i = 1
- while true do
- local name, value = debug.getupvalue(func, i)
- if not name then break end
- if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- if not written_vars[name] then
- debug.setupvalue(func, i, vars[name])
- end
- written_vars[name] = true
- end
- i = i + 1
- end
- end
- end
- --}}}
- --{{{ local function trace_event(event, line, level)
- local function print_trace(level,depth,event,file,line,name)
- --NB: level here is relative to the caller of trace_event, so offset by 2 to get to there
- level = level + 2
- local file = file or getinfo(level,'short_src')
- local line = line or getinfo(level,'currentline')
- local name = name or getinfo(level,'name')
- local prefix = ''
- if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
- io.write(prefix..
- string.format('%08.2f:%02i.',os.clock(),depth)..
- string.rep('.',depth%32)..
- (file or '')..' ('..(line or '')..') '..
- (name or '')..
- ' ('..event..')\n')
- end
- local function trace_event(event, line, level)
- if event == 'return' and trace_returns then
- --note the line info for later
- ret_file = getinfo(level+1,'short_src')
- ret_line = getinfo(level+1,'currentline')
- ret_name = getinfo(level+1,'name')
- end
- if event ~= 'line' then return end
- local slevel = stack_level[current_thread]
- local tlevel = trace_level[current_thread]
- if trace_calls and slevel > tlevel then
- --we are now in the function called, so look back 1 level further to find the calling file and line
- print_trace(level+1,slevel-1,'c',nil,nil,getinfo(level+1,'name'))
- end
- if trace_returns and slevel < tlevel then
- print_trace(level,slevel,'r',ret_file,ret_line,ret_name)
- end
- if trace_lines then
- print_trace(level,slevel,'l')
- end
- trace_level[current_thread] = stack_level[current_thread]
- end
- --}}}
- --{{{ local function report(ev, vars, file, line, idx_watch)
- local function report(ev, vars, file, line, idx_watch)
- function show_source()
- show(traceinfo[1].short_src, traceinfo[1].currentline, 2, 2)
- end
- local vars = vars or {}
- local file = file or '?'
- local line = line or 0
- local prefix = ''
- if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
- if ev == events.STEP then
- io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')\n')
- show_source()
- elseif ev == events.BREAK then
- io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..') (breakpoint)\n')
- show_source()
- elseif ev == events.WATCH then
- io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')'.." (watch expression "..idx_watch.. ": ["..watches[idx_watch].exp.."])\n")
- show_source()
- elseif ev == events.SET then
- --do nothing
- else
- io.write(prefix.."Error in application: "..file.." line "..line.."\n")
- end
- if ev ~= events.SET then
- if pausemsg and pausemsg ~= '' then io.write('Message: '..pausemsg..'\n') end
- pausemsg = ''
- end
- return vars, file, line
- end
- --}}}
- --{{{ local function debugger_loop(ev, vars, file, line, idx_watch)
- local last_line = ""
- local function debugger_loop(ev, vars, file, line, idx_watch)
- local eval_env = vars or {}
- local breakfile = file or '?'
- local breakline = line or 0
- local command, args
- --{{{ local function getargs(spec)
-
- --get command arguments according to the given spec from the args string
- --the spec has a single character for each argument, arguments are separated
- --by white space, the spec characters can be one of:
- -- F for a filename (defaults to breakfile if - given in args)
- -- L for a line number (defaults to breakline if - given in args)
- -- N for a number
- -- V for a variable name
- -- S for a string
-
- local function getargs(spec)
- local res={}
- local char,arg
- local ptr=1
- for i=1,string.len(spec) do
- char = string.sub(spec,i,i)
- if char == 'F' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '-' end
- if arg == '-' then arg = breakfile end
- elseif char == 'L' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '-' end
- if arg == '-' then arg = breakline end
- arg = tonumber(arg) or 0
- elseif char == 'N' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '0' end
- arg = tonumber(arg) or 0
- elseif char == 'V' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '' end
- elseif char == 'S' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '' end
- else
- arg = ''
- end
- table.insert(res,arg or '')
- end
- return unpack(res)
- end
-
- --}}}
- while true do
- io.write("[DEBUG]> ")
- local line = io.read("*line")
- if line == nil then io.write('\n'); line = 'exit' end
- if line == "" then
- line = last_line
- else
- last_line = line
- end
- io.write("'" .. last_line .. "'\n")
- if string.find(line, "^[a-z]+") then
- command = string.sub(line, string.find(line, "^[a-z]+"))
- args = string.gsub(line,"^[a-z]+%s*",'',1) --strip command off line
- else
- command = ''
- end
- command = aliases[command] or command
- if command == "setb" then
- --{{{ set breakpoint
-
- local line, filename = getargs('LF')
- if filename ~= '' and line ~= '' then
- set_breakpoint(filename,line)
- io.write("Breakpoint set in file "..filename..' line '..line..'\n')
- else
- io.write("Bad request\n")
- end
-
- --}}}
- elseif command == "delb" then
- --{{{ delete breakpoint
-
- local line, filename = getargs('LF')
- if filename ~= '' and line ~= '' then
- remove_breakpoint(filename, line)
- io.write("Breakpoint deleted from file "..filename..' line '..line.."\n")
- else
- io.write("Bad request\n")
- end
-
- --}}}
- elseif command == "delallb" then
- --{{{ delete all breakpoints
- breakpoints = {}
- io.write('All breakpoints deleted\n')
- --}}}
- elseif command == "listb" then
- --{{{ list breakpoints
- for i, v in pairs(breakpoints) do
- for ii, vv in pairs(v) do
- io.write("Break at: "..i..' in '..ii..'\n')
- end
- end
- --}}}
- elseif command == "setw" then
- --{{{ set watch expression
-
- if args and args ~= '' then
- local func = loadstring("return(" .. args .. ")")
- local newidx = #watches + 1
- watches[newidx] = {func = func, exp = args}
- io.write("Set watch exp no. " .. newidx..'\n')
- else
- io.write("Bad request\n")
- end
-
- --}}}
- elseif command == "delw" then
- --{{{ delete watch expression
-
- local index = tonumber(args)
- if index then
- watches[index] = nil
- io.write("Watch expression deleted\n")
- else
- io.write("Bad request\n")
- end
-
- --}}}
- elseif command == "delallw" then
- --{{{ delete all watch expressions
- watches = {}
- io.write('All watch expressions deleted\n')
- --}}}
- elseif command == "listw" then
- --{{{ list watch expressions
- for i, v in pairs(watches) do
- io.write("Watch exp. " .. i .. ": " .. v.exp..'\n')
- end
- --}}}
- elseif command == "run" then
- --{{{ run until breakpoint
- step_into = false
- step_over = false
- return 'cont'
- --}}}
- elseif command == "step" then
- --{{{ step N lines (into functions)
- local N = tonumber(args) or 1
- step_over = false
- step_into = true
- step_lines = tonumber(N or 1)
- return 'cont'
- --}}}
- elseif command == "over" then
- --{{{ step N lines (over functions)
- local N = tonumber(args) or 1
- step_into = false
- step_over = true
- step_lines = tonumber(N or 1)
- step_level[current_thread] = stack_level[current_thread]
- return 'cont'
- --}}}
- elseif command == "out" then
- --{{{ step N lines (out of functions)
- local N = tonumber(args) or 1
- step_into = false
- step_over = true
- step_lines = 1
- step_level[current_thread] = stack_level[current_thread] - tonumber(N or 1)
- return 'cont'
- --}}}
- elseif command == "gotoo" then
- --{{{ step until reach line
- local line, filename = getargs('LF')
- if line ~= '' then
- step_over = false
- step_into = false
- if has_breakpoint(filename,line) then
- return 'cont'
- else
- set_breakpoint(filename,line,true)
- return 'cont'
- end
- else
- io.write("Bad request\n")
- end
- --}}}
- elseif command == "set" then
- --{{{ set/show context level
- local level = args
- if level and level == '' then level = nil end
- if level then return level end
- --}}}
- elseif command == "vars" then
- --{{{ list context variables
- local depth = args
- if depth and depth == '' then depth = nil end
- depth = tonumber(depth) or 1
- dumpvar(eval_env, depth+1, 'variables')
- --}}}
- elseif command == "glob" then
- --{{{ list global variables
- local depth = args
- if depth and depth == '' then depth = nil end
- depth = tonumber(depth) or 1
- dumpvar(eval_env.__GLOBALS__,depth+1,'globals')
- --}}}
- elseif command == "fenv" then
- --{{{ list function environment variables
- local depth = args
- if depth and depth == '' then depth = nil end
- depth = tonumber(depth) or 1
- dumpvar(eval_env.__ENVIRONMENT__,depth+1,'environment')
- --}}}
- elseif command == "ups" then
- --{{{ list upvalue names
- dumpvar(eval_env.__UPVALUES__,2,'upvalues')
- --}}}
- elseif command == "locs" then
- --{{{ list locals names
- dumpvar(eval_env.__LOCALS__,2,'upvalues')
- --}}}
- elseif command == "what" then
- --{{{ show where a function is defined
- if args and args ~= '' then
- local v = eval_env
- local n = nil
- for w in string.gmatch(args,"[%w_]+") do
- v = v[w]
- if n then n = n..'.'..w else n = w end
- if not v then break end
- end
- if type(v) == 'function' then
- local def = debug.getinfo(v,'S')
- if def then
- io.write(def.what..' in '..def.short_src..' '..def.linedefined..'..'..def.lastlinedefined..'\n')
- else
- io.write('Cannot get info for '..v..'\n')
- end
- else
- io.write(v..' is not a function\n')
- end
- else
- io.write("Bad request\n")
- end
- --}}}
- elseif command == "dump" then
- --{{{ dump a variable
- local name, depth = getargs('VN')
- if name ~= '' then
- if depth == '' or depth == 0 then depth = nil end
- depth = tonumber(depth or 1)
- local v = eval_env
- local n = nil
- for w in string.gmatch(name,"[^%.]+") do --get everything between dots
- if tonumber(w) then
- v = v[tonumber(w)]
- else
- v = v[w]
- end
- if n then n = n..'.'..w else n = w end
- if not v then break end
- end
- dumpvar(v,depth+1,n)
- else
- io.write("Bad request\n")
- end
- --}}}
- elseif command == "show" then
- --{{{ show file around a line or the current breakpoint
-
- local line, file, before, after = getargs('LFNN')
- if before == 0 then before = 10 end
- if after == 0 then after = before end
-
- if file ~= '' and file ~= "=stdin" then
- show(file,line,before,after)
- else
- io.write('Nothing to show\n')
- end
-
- --}}}
- elseif command == "poff" then
- --{{{ turn pause command off
- pause_off = true
- --}}}
- elseif command == "pon" then
- --{{{ turn pause command on
- pause_off = false
- --}}}
- elseif command == "tron" then
- --{{{ turn tracing on/off
- local option = getargs('S')
- trace_calls = false
- trace_returns = false
- trace_lines = false
- if string.find(option,'c') then trace_calls = true end
- if string.find(option,'r') then trace_returns = true end
- if string.find(option,'l') then trace_lines = true end
- --}}}
- elseif command == "trace" then
- --{{{ dump a stack trace
- trace(eval_env.__VARSLEVEL__)
- --}}}
- elseif command == "info" then
- --{{{ dump all debug info captured
- info()
- --}}}
- elseif command == "pause" then
- --{{{ not allowed in here
- io.write('pause() should only be used in the script you are debugging\n')
- --}}}
- elseif command == "help" then
- --{{{ help
- local command = getargs('S')
- if command ~= '' and hints[command] then
- io.write(hints[command]..'\n')
- else
- for _,v in pairs(hints) do
- local _,_,h = string.find(v,"(.+)|")
- io.write(h..'\n')
- end
- end
- --}}}
- elseif command == "exit" then
- --{{{ exit debugger
- return 'stop'
- --}}}
- elseif line ~= '' then
- --{{{ just execute whatever it is in the current context
-
- --map line starting with "=..." to "return ..."
- if string.sub(line,1,1) == '=' then line = string.gsub(line,'=','return ',1) end
-
- local ok, func = pcall(loadstring,line)
- if func == nil then [email protected]
- io.write("Compile error: "..line..'\n')
- elseif not ok then
- io.write("Compile error: "..func..'\n')
- else
- setfenv(func, eval_env)
- local res = {pcall(func)}
- if res[1] then
- if res[2] then
- table.remove(res,1)
- for _,v in ipairs(res) do
- io.write(tostring(v))
- io.write('\t')
- end
- io.write('\n')
- end
- --update in the context
- return 0
- else
- io.write("Run error: "..res[2]..'\n')
- end
- end
-
- --}}}
- end
- end
- end
- --}}}
- --{{{ local function debug_hook(event, line, level, thread)
- local function debug_hook(event, line, level, thread)
- if not started then debug.sethook(); coro_debugger = nil; return end
- current_thread = thread or 'main'
- local level = level or 2
- trace_event(event,line,level)
- if event == "call" then
- stack_level[current_thread] = stack_level[current_thread] + 1
- elseif event == "return" then
- stack_level[current_thread] = stack_level[current_thread] - 1
- if stack_level[current_thread] < 0 then stack_level[current_thread] = 0 end
- else
- local vars,file,line = capture_vars(level,1,line)
- local stop, ev, idx = false, events.STEP, 0
- while true do
- for index, value in pairs(watches) do
- setfenv(value.func, vars)
- local status, res = pcall(value.func)
- if status and res then
- ev, idx = events.WATCH, index
- stop = true
- break
- end
- end
- if stop then break end
- if (step_into)
- or (step_over and (stack_level[current_thread] <= step_level[current_thread] or stack_level[current_thread] == 0)) then
- step_lines = step_lines - 1
- if step_lines < 1 then
- ev, idx = events.STEP, 0
- break
- end
- end
- if has_breakpoint(file, line) then
- ev, idx = events.BREAK, 0
- break
- end
- return
- end
- tracestack(level)
- if not coro_debugger then
- io.write("\nLua Debugger\n")
- vars, file, line = report(ev, vars, file, line, idx)
- io.write("Type 'help' for commands\n")
- coro_debugger = true
- else
- vars, file, line = report(ev, vars, file, line, idx)
- end
- local last_next = 1
- local next = 'ask'
- local silent = false
- while true do
- if next == 'ask' then
- next = debugger_loop(ev, vars, file, line, idx)
- elseif next == 'cont' then
- return
- elseif next == 'stop' then
- started = false
- debug.sethook()
- coro_debugger = nil
- return
- elseif tonumber(next) then --get vars for given level or last level
- next = tonumber(next)
- if next == 0 then silent = true; next = last_next else silent = false end
- last_next = next
- restore_vars(level,vars)
- vars, file, line = capture_vars(level,next)
- if not silent then
- if vars and vars.__VARSLEVEL__ then
- io.write('Level: '..vars.__VARSLEVEL__..'\n')
- else
- io.write('No level set\n')
- end
- end
- ev = events.SET
- next = 'ask'
- else
- io.write('Unknown command from debugger_loop: '..tostring(next)..'\n')
- io.write('Stopping debugger\n')
- next = 'stop'
- end
- end
- end
- end
- --}}}
- --{{{ coroutine.create
- --This function overrides the built-in for the purposes of propagating
- --the debug hook settings from the creator into the created coroutine.
- _G.coroutine.create = function(f)
- local thread
- local hook, mask, count = debug.gethook()
- if hook then
- local function thread_hook(event,line)
- hook(event,line,3,thread)
- end
- thread = cocreate(function(...)
- stack_level[thread] = 0
- trace_level[thread] = 0
- step_level [thread] = 0
- debug.sethook(thread_hook,mask,count)
- return f(...)
- end)
- return thread
- else
- return cocreate(f)
- end
- end
- --}}}
- --{{{ coroutine.wrap
- --This function overrides the built-in for the purposes of propagating
- --the debug hook settings from the creator into the created coroutine.
- _G.coroutine.wrap = function(f)
- local thread
- local hook, mask, count = debug.gethook()
- if hook then
- local function thread_hook(event,line)
- hook(event,line,3,thread)
- end
- thread = cowrap(function(...)
- stack_level[thread] = 0
- trace_level[thread] = 0
- step_level [thread] = 0
- debug.sethook(thread_hook,mask,count)
- return f(...)
- end)
- return thread
- else
- return cowrap(f)
- end
- end
- --}}}
- --{{{ function pause(x,l,f)
- --
- -- Starts/resumes a debug session
- --
- function pause(x,l,f)
- if not f and pause_off then return end --being told to ignore pauses
- pausemsg = x or 'pause'
- local lines
- local src = getinfo(2,'short_src')
- if l then
- lines = l --being told when to stop
- elseif src == "stdin" then
- lines = 1 --if in a console session, stop now
- else
- lines = 2 --if in a script, stop when get out of pause()
- end
- if started then
- --we'll stop now 'cos the existing debug hook will grab us
- step_lines = lines
- step_into = true
- debug.sethook(debug_hook, "crl") --reset it in case some external agent fiddled with it
- else
- --set to stop when get out of pause()
- trace_level[current_thread] = 0
- step_level [current_thread] = 0
- stack_level[current_thread] = 1
- step_lines = lines
- step_into = true
- started = true
- debug.sethook(debug_hook, "crl") --NB: this will cause an immediate entry to the debugger_loop
- end
- end
- --}}}
- --{{{ function dump(v,depth)
- --shows the value of the given variable, only really useful
- --when the variable is a table
- --see dump debug command hints for full semantics
- function dump(v,depth)
- dumpvar(v,(depth or 1)+1,tostring(v))
- end
- --}}}
- --{{{ function debug.traceback(x)
- local _traceback = debug.traceback --note original function
- --override standard function
- debug.traceback = function(x)
- local assertmsg = _traceback(x) --do original function
- pause(x) --let user have a look at stuff
- return assertmsg --carry on
- end
- _TRACEBACK = debug.traceback --Lua 5.0 function
- --}}}
- --------------------------------------------------------------------------------
- -- dir.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- function dir_match_generator_impl(text)
- -- Strip off any path components that may be on text.
- local prefix = ""
- local i = text:find("[\\/:][^\\/:]*$")
- if i then
- prefix = text:sub(1, i)
- end
- local include_dots = text:find("%.+$") ~= nil
- local matches = {}
- local mask = text.."*"
- -- Find matches.
- for _, dir in ipairs(clink.find_dirs(mask, true)) do
- local file = prefix..dir
- if include_dots or (dir ~= "." and dir ~= "..") then
- if clink.is_match(text, file) then
- table.insert(matches, prefix..dir)
- end
- end
- end
- return matches
- end
- --------------------------------------------------------------------------------
- local function dir_match_generator(word)
- local matches = dir_match_generator_impl(word)
- -- If there was no matches but text is a dir then use it as the single match.
- -- Otherwise tell readline that matches are files and it will do magic.
- if #matches == 0 then
- if clink.is_dir(rl_state.text) then
- table.insert(matches, rl_state.text)
- end
- else
- clink.matches_are_files()
- end
- return matches
- end
- --------------------------------------------------------------------------------
- clink.arg.register_parser("cd", dir_match_generator)
- clink.arg.register_parser("chdir", dir_match_generator)
- clink.arg.register_parser("pushd", dir_match_generator)
- clink.arg.register_parser("rd", dir_match_generator)
- clink.arg.register_parser("rmdir", dir_match_generator)
- clink.arg.register_parser("md", dir_match_generator)
- clink.arg.register_parser("mkdir", dir_match_generator)
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- env.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local special_env_vars = {
- "cd", "date", "time", "random", "errorlevel",
- "cmdextversion", "cmdcmdline", "highestnumanodenumber"
- }
- --------------------------------------------------------------------------------
- local function env_vars_display_filter(matches)
- local to_display = {}
- for _, m in ipairs(matches) do
- local _, _, out = m:find("(%%[^%%]+%%)$")
- table.insert(to_display, out)
- end
- return to_display
- end
- --------------------------------------------------------------------------------
- local function env_vars_find_matches(candidates, prefix, part)
- local part_len = #part
- for _, name in ipairs(candidates) do
- if clink.lower(name:sub(1, part_len)) == part then
- clink.add_match(prefix..'%'..name:lower()..'%')
- end
- end
- end
- --------------------------------------------------------------------------------
- local function env_vars_match_generator(text, first, last)
- local all = rl_state.line_buffer:sub(1, last)
- -- Skip pairs of %s
- local i = 1
- for _, r in function () return all:find("%b%%", i) end do
- i = r + 2
- end
- -- Find a solitary %
- local i = all:find("%%", i)
- if not i then
- return false
- end
- if i < first then
- return false
- end
- local part = clink.lower(all:sub(i + 1))
- local part_len = #part
- i = i - first
- local prefix = text:sub(1, i)
- env_vars_find_matches(clink.get_env_var_names(), prefix, part)
- env_vars_find_matches(special_env_vars, prefix, part)
- if clink.match_count() >= 1 then
- clink.match_display_filter = env_vars_display_filter
- clink.suppress_char_append()
- clink.suppress_quoting()
- return true
- end
- return false
- end
- --------------------------------------------------------------------------------
- if clink.get_host_process() == "cmd.exe" then
- clink.register_match_generator(env_vars_match_generator, 10)
- end
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- exec.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local dos_commands = {
- "assoc", "break", "call", "cd", "chcp", "chdir", "cls", "color", "copy",
- "date", "del", "dir", "diskcomp", "diskcopy", "echo", "endlocal", "erase",
- "exit", "for", "format", "ftype", "goto", "graftabl", "if", "md", "mkdir",
- "mklink", "more", "move", "path", "pause", "popd", "prompt", "pushd", "rd",
- "rem", "ren", "rename", "rmdir", "set", "setlocal", "shift", "start",
- "time", "title", "tree", "type", "ver", "verify", "vol"
- }
- --------------------------------------------------------------------------------
- local function get_environment_paths()
- local paths = clink.split(clink.get_env("PATH"), ";")
- -- We're expecting absolute paths and as ';' is a valid path character
- -- there maybe unneccessary splits. Here we resolve them.
- local paths_merged = { paths[1] }
- for i = 2, #paths, 1 do
- if not paths[i]:find("^[a-zA-Z]:") then
- local t = paths_merged[#paths_merged];
- paths_merged[#paths_merged] = t..paths[i]
- else
- table.insert(paths_merged, paths[i])
- end
- end
- -- Append slashes.
- for i = 1, #paths_merged, 1 do
- paths_merged[i] = paths_merged[i].."/"
- end
- return paths_merged
- end
- --------------------------------------------------------------------------------
- local function exec_find_dirs(pattern, case_map)
- local ret = {}
- for _, dir in ipairs(clink.find_dirs(pattern, case_map)) do
- if dir ~= "." and dir ~= ".." then
- table.insert(ret, dir)
- end
- end
- return ret
- end
- --------------------------------------------------------------------------------
- local function exec_match_generator(text, first, last)
- -- If match style setting is < 0 then consider executable matching disabled.
- local match_style = clink.get_setting_int("exec_match_style")
- if match_style < 0 then
- return false
- end
- -- We're only interested in exec completion if this is the first word of the
- -- line, or the first word after a command separator.
- if clink.get_setting_int("space_prefix_match_files") > 0 then
- if first > 1 then
- return false
- end
- else
- local leading = rl_state.line_buffer:sub(1, first - 1)
- local is_first = leading:find("^%s*\"*$")
- if not is_first then
- return false
- end
- end
- -- Split text into directory and name
- local text_dir = ""
- local text_name = text
- local i = text:find("[\\/:][^\\/:]*$")
- if i then
- text_dir = text:sub(1, i)
- text_name = text:sub(i + 1)
- end
- local paths
- if not text:find("[\\/:]") then
- -- If the terminal is cmd.exe check it's commands for matches.
- if clink.get_host_process() == "cmd.exe" then
- clink.match_words(text, dos_commands)
- end
- -- Add console aliases as matches.
- local aliases = clink.get_console_aliases()
- clink.match_words(text, aliases)
- paths = get_environment_paths();
- else
- paths = {}
- -- 'text' is an absolute or relative path. If we're doing Bash-style
- -- matching should now consider directories.
- if match_style < 1 then
- match_style = 2
- else
- match_style = 1
- end
- end
- -- Should we also consider the path referenced by 'text'?
- if match_style >= 1 then
- table.insert(paths, text_dir)
- end
- -- Search 'paths' for files ending in 'suffices' and look for matches
- local suffices = clink.split(clink.get_env("pathext"), ";")
- for _, suffix in ipairs(suffices) do
- for _, path in ipairs(paths) do
- local files = clink.find_files(path.."*"..suffix, false)
- for _, file in ipairs(files) do
- if clink.is_match(text_name, file) then
- clink.add_match(text_dir..file)
- end
- end
- end
- end
- -- Lastly we may wish to consider directories too.
- if clink.match_count() == 0 or match_style >= 2 then
- clink.match_files(text.."*", true, exec_find_dirs)
- end
- clink.matches_are_files()
- return true
- end
- --------------------------------------------------------------------------------
- clink.register_match_generator(exec_match_generator, 50)
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- git.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local git_argument_tree = {
- -- Porcelain and ancillary commands from git's man page.
- "add", "am", "archive", "bisect", "branch", "bundle", "checkout",
- "cherry-pick", "citool", "clean", "clone", "commit", "describe", "diff",
- "fetch", "format-patch", "gc", "grep", "gui", "init", "log", "merge", "mv",
- "notes", "pull", "push", "rebase", "reset", "revert", "rm", "shortlog",
- "show", "stash", "status", "submodule", "tag", "config", "fast-export",
- "fast-import", "filter-branch", "lost-found", "mergetool", "pack-refs",
- "prune", "reflog", "relink", "remote", "repack", "replace", "repo-config",
- "annotate", "blame", "cherry", "count-objects", "difftool", "fsck",
- "get-tar-commit-id", "help", "instaweb", "merge-tree", "rerere",
- "rev-parse", "show-branch", "verify-tag", "whatchanged"
- }
- clink.arg.register_parser("git", git_argument_tree)
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- go.lua
- --
- --
- -- Copyright (c) 2013 Dobroslaw Zybort
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local function flags(...)
- local p = clink.arg.new_parser()
- p:set_flags(...)
- return p
- end
- --------------------------------------------------------------------------------
- local go_tool_parser = clink.arg.new_parser()
- go_tool_parser:set_flags("-n")
- go_tool_parser:set_arguments({
- "8a", "8c", "8g", "8l", "addr2line", "cgo", "dist", "nm", "objdump",
- "pack",
- "cover" .. flags("-func", "-html", "-mode", "-o", "-var"),
- "fix" .. flags("-diff", "-force", "-r"),
- "prof" .. flags("-p", "-t", "-d", "-P", "-h", "-f", "-l", "-r", "-s",
- "-hs"),
- "pprof" .. flags(-- Options:
- "--cum", "--base", "--interactive", "--seconds",
- "--add_lib", "--lib_prefix",
- -- Reporting Granularity:
- "--addresses", "--lines", "--functions", "--files",
- -- Output type:
- "--text", "--callgrind", "--gv", "--web", "--list",
- "--disasm", "--symbols", "--dot", "--ps", "--pdf",
- "--svg", "--gif", "--raw",
- -- Heap-Profile Options:
- "--inuse_space", "--inuse_objects", "--alloc_space",
- "--alloc_objects", "--show_bytes", "--drop_negative",
- -- Contention-profile options:
- "--total_delay", "--contentions", "--mean_delay",
- -- Call-graph Options:
- "--nodecount", "--nodefraction", "--edgefraction",
- "--focus", "--ignore", "--scale", "--heapcheck",
- -- Miscellaneous:
- "--tools", "--test", "--help", "--version"),
- "vet" .. flags("-all", "-asmdecl", "-assign", "-atomic", "-buildtags",
- "-composites", "-compositewhitelist", "-copylocks",
- "-methods", "-nilfunc", "-printf", "-printfuncs",
- "-rangeloops", "-shadow", "-shadowstrict", "-structtags",
- "-test", "-unreachable", "-v"),
- "yacc" .. flags("-l", "-o", "-p", "-v"),
- })
- --------------------------------------------------------------------------------
- local go_parser = clink.arg.new_parser()
- go_parser:set_arguments({
- "env",
- "fix",
- "version",
- "build" .. flags("-o", "-a", "-n", "-p", "-installsuffix", "-v", "-x",
- "-work", "-gcflags", "-ccflags", "-ldflags",
- "-gccgoflags", "-tags", "-compiler", "-race"),
- "clean" .. flags("-i", "-n", "-r", "-x"),
- "fmt" .. flags("-n", "-x"),
- "get" .. flags("-d", "-fix", "-t", "-u",
- -- Build flags
- "-a", "-n", "-p", "-installsuffix", "-v", "-x",
- "-work", "-gcflags", "-ccflags", "-ldflags",
- "-gccgoflags", "-tags", "-compiler", "-race"),
- "install" .. flags(-- All `go build` flags
- "-o", "-a", "-n", "-p", "-installsuffix", "-v", "-x",
- "-work", "-gcflags", "-ccflags", "-ldflags",
- "-gccgoflags", "-tags", "-compiler", "-race"),
- "list" .. flags("-e", "-race", "-f", "-json", "-tags"),
- "run" .. flags("-exec",
- -- Build flags
- "-a", "-n", "-p", "-installsuffix", "-v", "-x",
- "-work", "-gcflags", "-ccflags", "-ldflags",
- "-gccgoflags", "-tags", "-compiler", "-race"),
- "test" .. flags(-- Local.
- "-c", "-file", "-i", "-cover", "-coverpkg",
- -- Build flags
- "-a", "-n", "-p", "-x", "-work", "-ccflags",
- "-gcflags", "-exec", "-ldflags", "-gccgoflags",
- "-tags", "-compiler", "-race", "-installsuffix",
- -- Passed to 6.out
- "-bench", "-benchmem", "-benchtime", "-covermode",
- "-coverprofile", "-cpu", "-cpuprofile", "-memprofile",
- "-memprofilerate", "-blockprofile",
- "-blockprofilerate", "-outputdir", "-parallel", "-run",
- "-short", "-timeout", "-v"),
- "tool" .. go_tool_parser,
- "vet" .. flags("-n", "-x"),
- })
- --------------------------------------------------------------------------------
- local go_help_parser = clink.arg.new_parser()
- go_help_parser:set_arguments({
- "help" .. clink.arg.new_parser():set_arguments({
- go_parser:flatten_argument(1)
- })
- })
- --------------------------------------------------------------------------------
- local godoc_parser = clink.arg.new_parser()
- godoc_parser:set_flags(
- "-zip", "-write_index", "-analysis", "-http", "-server", "-html","-src",
- "-url", "-q", "-v", "-goroot", "-tabwidth", "-timestamps", "-templates",
- "-play", "-ex", "-links", "-index", "-index_files", "-maxresults",
- "-index_throttle", "-notes", "-httptest.serve"
- )
- --------------------------------------------------------------------------------
- local gofmt_parser = clink.arg.new_parser()
- gofmt_parser:set_flags(
- "-cpuprofile", "-d", "-e", "-l", "-r", "-s", "-w"
- )
- --------------------------------------------------------------------------------
- clink.arg.register_parser("go", go_parser)
- clink.arg.register_parser("go", go_help_parser)
- clink.arg.register_parser("godoc", godoc_parser)
- clink.arg.register_parser("gofmt", gofmt_parser)
- --------------------------------------------------------------------------------
- -- hg.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local hg_tree = {
- "add", "addremove", "annotate", "archive", "backout", "bisect", "bookmarks",
- "branch", "branches", "bundle", "cat", "clone", "commit", "copy", "diff",
- "export", "forget", "grep", "heads", "help", "identify", "import",
- "incoming", "init", "locate", "log", "manifest", "merge", "outgoing",
- "parents", "paths", "pull", "push", "recover", "remove", "rename", "resolve",
- "revert", "rollback", "root", "serve", "showconfig", "status", "summary",
- "tag", "tags", "tip", "unbundle", "update", "verify", "version", "graft",
- "phases"
- }
- clink.arg.register_parser("hg", hg_tree)
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- p4.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local p4_tree = {
- "add", "annotate", "attribute", "branch", "branches", "browse", "change",
- "changes", "changelist", "changelists", "client", "clients", "copy",
- "counter", "counters", "cstat", "delete", "depot", "depots", "describe",
- "diff", "diff2", "dirs", "edit", "filelog", "files", "fix", "fixes",
- "flush", "fstat", "grep", "group", "groups", "have", "help", "info",
- "integrate", "integrated", "interchanges", "istat", "job", "jobs", "label",
- "labels", "labelsync", "legal", "list", "lock", "logger", "login",
- "logout", "merge", "move", "opened", "passwd", "populate", "print",
- "protect", "protects", "reconcile", "rename", "reopen", "resolve",
- "resolved", "revert", "review", "reviews", "set", "shelve", "status",
- "sizes", "stream", "streams", "submit", "sync", "tag", "tickets", "unlock",
- "unshelve", "update", "user", "users", "where", "workspace", "workspaces"
- }
- clink.arg.register_parser("p4", p4_tree)
- --------------------------------------------------------------------------------
- local p4vc_tree = {
- "help", "branchmappings", "branches", "diff", "groups", "branch", "change",
- "client", "workspace", "depot", "group", "job", "label", "user", "jobs",
- "labels", "pendingchanges", "resolve", "revisiongraph", "revgraph",
- "streamgraph", "streams", "submit", "submittedchanges", "timelapse",
- "timelapseview", "tlv", "users", "workspaces", "clients", "shutdown"
- }
- clink.arg.register_parser("p4vc", p4vc_tree)
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- powershell.lua
- --
- --
- -- Copyright (c) 2013 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local function powershell_prompt_filter()
- local l, r, path = clink.prompt.value:find("([a-zA-Z]:\\.*)> $")
- if path ~= nil then
- clink.chdir(path)
- end
- end
- --------------------------------------------------------------------------------
- if clink.get_host_process() == "powershell.exe" then
- clink.prompt.register_filter(powershell_prompt_filter, -493)
- end
- --------------------------------------------------------------------------------
- -- self.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local null_parser = clink.arg.new_parser()
- null_parser:disable_file_matching()
- local inject_parser = clink.arg.new_parser()
- inject_parser:set_flags(
- "--help",
- "--nohostcheck",
- "--pid",
- "--profile",
- "--quiet",
- "--scripts"
- )
- local autorun_dashdash_parser = clink.arg.new_parser()
- autorun_dashdash_parser:set_arguments({ "--" .. inject_parser })
- local autorun_parser = clink.arg.new_parser()
- autorun_parser:set_flags("--allusers", "--help")
- autorun_parser:set_arguments(
- {
- "install" .. autorun_dashdash_parser,
- "uninstall" .. null_parser,
- "show" .. null_parser,
- "set"
- }
- )
- local set_parser = clink.arg.new_parser()
- set_parser:disable_file_matching()
- set_parser:set_flags("--help")
- set_parser:set_arguments(
- {
- "ansi_code_support",
- "ctrld_exits",
- "esc_clears_line",
- "exec_match_style",
- "history_dupe_mode",
- "history_expand_mode",
- "history_file_lines",
- "history_ignore_space",
- "history_io",
- "match_colour",
- "prompt_colour",
- "space_prefix_match_files",
- "strip_crlf_on_paste",
- "terminate_autoanswer",
- "use_altgr_substitute",
- }
- )
- local self_parser = clink.arg.new_parser()
- self_parser:set_arguments(
- {
- "inject" .. inject_parser,
- "autorun" .. autorun_parser,
- "set" .. set_parser,
- }
- )
- clink.arg.register_parser("clink", self_parser)
- clink.arg.register_parser("clink_x86", self_parser)
- clink.arg.register_parser("clink_x64", self_parser)
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- set.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local function set_match_generator(word)
- -- Skip this generator if first is in the rvalue.
- local leading = rl_state.line_buffer:sub(1, rl_state.first - 1)
- if leading:find("=") then
- return false
- end
- -- Enumerate environment variables and check for potential matches.
- local matches = {}
- for _, name in ipairs(clink.get_env_var_names()) do
- if clink.is_match(word, name) then
- table.insert(matches, name:lower())
- end
- end
- clink.suppress_char_append()
- return matches
- end
- --------------------------------------------------------------------------------
- clink.arg.register_parser("set", set_match_generator)
- -- vim: expandtab
- --------------------------------------------------------------------------------
- -- svn.lua
- --
- --
- -- Copyright (c) 2012 Martin Ridgers
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- --
- --------------------------------------------------------------------------------
- local svn_tree = {
- "add", "blame", "praise", "annotate", "ann", "cat", "changelist", "cl",
- "checkout", "co", "cleanup", "commit", "ci", "copy", "cp", "delete", "del",
- "remove", "rm", "diff", "di", "export", "help", "h", "import", "info",
- "list", "ls", "lock", "log", "merge", "mergeinfo", "mkdir", "move", "mv",
- "rename", "ren", "propdel", "pdel", "pd", "propedit", "pedit", "pe",
- "propget", "pget", "pg", "proplist", "plist", "pl", "propset", "pset", "ps",
- "resolve", "resolved", "revert", "status", "stat", "st", "switch", "sw",
- "unlock", "update", "up"
- }
- clink.arg.register_parser("svn", svn_tree)
- -- vim: expandtab
|