diff --git a/app/src/main/assets/editor/beautify/beautify.min.js b/app/src/main/assets/editor/beautify/beautify.min.js deleted file mode 100644 index 4ee7ecac..00000000 --- a/app/src/main/assets/editor/beautify/beautify.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e=function(e){function t(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=6)}([function(e,t){var n="ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԧԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠࢢ-ࢬऄ-हऽॐक़-ॡॱ-ॷॹ-ॿঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛰᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々-〇〡-〩〱-〵〸-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚗꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꪀ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ",i=new RegExp("["+n+"]"),r=new RegExp("["+n+"̀-ͯ҃-֑҇-ׇֽֿׁׂׅׄؐ-ؚؠ-ىٲ-ۓۧ-ۨۻ-ۼܰ-݊ࠀ-ࠔࠛ-ࠣࠥ-ࠧࠩ-࠭ࡀ-ࡗࣤ-ࣾऀ-ःऺ-़ा-ॏ॑-ॗॢ-ॣ०-९ঁ-ঃ়া-ৄেৈৗয়-ৠਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑ੦-ੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢ-ૣ૦-૯ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୟ-ୠ୦-୯ஂா-ூெ-ைொ-்ௗ௦-௯ఁ-ఃె-ైొ-్ౕౖౢ-ౣ౦-౯ಂಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢ-ೣ೦-೯ംഃെ-ൈൗൢ-ൣ൦-൯ංඃ්ා-ුූෘ-ෟෲෳิ-ฺเ-ๅ๐-๙ິ-ູ່-ໍ໐-໙༘༙༠-༩༹༵༷ཁ-ཇཱ-྄྆-྇ྍ-ྗྙ-ྼ࿆က-ဩ၀-၉ၧ-ၭၱ-ၴႂ-ႍႏ-ႝ፝-፟ᜎ-ᜐᜠ-ᜰᝀ-ᝐᝲᝳក-ឲ៝០-៩᠋-᠍᠐-᠙ᤠ-ᤫᤰ-᤻ᥑ-ᥭᦰ-ᧀᧈ-ᧉ᧐-᧙ᨀ-ᨕᨠ-ᩓ᩠-᩿᩼-᪉᪐-᪙ᭆ-ᭋ᭐-᭙᭫-᭳᮰-᮹᯦-᯳ᰀ-ᰢ᱀-᱉ᱛ-ᱽ᳐-᳒ᴀ-ᶾḁ-ἕ‌‍‿⁀⁔⃐-⃥⃜⃡-⃰ⶁ-ⶖⷠ-ⷿ〡-〨゙゚Ꙁ-ꙭꙴ-꙽ꚟ꛰-꛱ꟸ-ꠀ꠆ꠋꠣ-ꠧꢀ-ꢁꢴ-꣄꣐-꣙ꣳ-ꣷ꤀-꤉ꤦ-꤭ꤰ-ꥅꦀ-ꦃ꦳-꧀ꨀ-ꨧꩀ-ꩁꩌ-ꩍ꩐-꩙ꩻꫠ-ꫩꫲ-ꫳꯀ-ꯡ꯬꯭꯰-꯹ﬠ-ﬨ︀-️︠-︦︳︴﹍-﹏0-9_]");t.newline=/[\n\r\u2028\u2029]/,t.lineBreak=new RegExp("\r\n|"+t.newline.source),t.allLineBreaks=new RegExp(t.lineBreak.source,"g"),t.isIdentifierStart=function(e){return e<65?36===e||64===e:e<91||(e<97?95===e:e<123||e>=170&&i.test(String.fromCharCode(e)))},t.isIdentifierChar=function(e){return e<48?36===e:e<58||!(e<65)&&(e<91||(e<97?95===e:e<123||e>=170&&r.test(String.fromCharCode(e))))}},function(e,t,n){function i(e,t){if(!t.multiline_frame&&t.mode!==h.ForInitializer&&t.mode!==h.Conditional){var n=t.start_line_index;e.remove_indent(n)}}function r(e,t){for(var n=0;nn&&(n=e.line_indent_level)),{mode:t,parent:e,last_text:e?e.last_text:"",last_word:e?e.last_word:"",declaration_statement:!1,declaration_assignment:!1,multiline_frame:!1,inline_frame:!1,if_block:!1,else_block:!1,do_block:!1,do_while:!1,import_block:!1,in_case_statement:!1,in_case:!1,case_body:!1,indentation_level:n,line_indent_level:e?e.line_indent_level:n,start_line_index:L.get_line_number(),ternary_depth:0}}function f(e,t){for(var n=e.newlines,i=Q.keep_array_indentation&&k(W.mode),r=B,_=0;_0,t);else if(Q.max_preserve_newlines&&n>Q.max_preserve_newlines&&(n=Q.max_preserve_newlines),Q.preserve_newlines&&e.newlines>1){x(!1,t);for(var o=1;o=Q.wrap_line_length&&x(!1,!0)}}}function x(e,t){if(!t&&";"!==W.last_text&&","!==W.last_text&&"="!==W.last_text&&"TK_OPERATOR"!==M)for(var n=C(1);!(W.mode!==h.Statement||W.if_block&&n&&"TK_RESERVED"===n.type&&"else"===n.text||W.do_block);)O();L.add_new_line(e)&&(W.multiline_frame=!0)}function R(){L.just_added_newline()&&(Q.keep_array_indentation&&k(W.mode)&&B.wanted_newline?(L.current_line.push(B.whitespace_before),L.space_before_token=!1):L.set_indent(W.indentation_level)&&(W.line_indent_level=W.indentation_level))}function K(e){if(L.raw)L.add_raw_token(B);else{if(Q.comma_first&&"TK_COMMA"===M&&L.just_added_newline()&&","===L.previous_line.last()){var t=L.previous_line.pop();L.previous_line.is_empty()&&(L.previous_line.push(t),L.trim(!0),L.current_line.pop(),L.trim()),R(),L.add_token(","),L.space_before_token=!0}e=e||B.text,R(),L.add_token(e)}}function m(){W.indentation_level+=1}function b(){W.indentation_level>0&&(!W.parent||W.indentation_level>W.parent.indentation_level)&&(W.indentation_level-=1)}function v(e){W?(z.push(W),X=W):X=n(null,e),W=n(X,e)}function k(e){return e===h.ArrayLiteral}function w(e){return r(e,[h.Expression,h.ForInitializer,h.Conditional])}function O(){z.length>0&&(X=W,W=z.pop(),X.mode!==h.Statement||Q.unindent_chained_methods||i(L,X))}function S(){return W.parent.mode===h.ObjectLiteral&&W.mode===h.Statement&&(":"===W.last_text&&0===W.ternary_depth||"TK_RESERVED"===M&&r(W.last_text,["get","set"]))}function y(){return!!("TK_RESERVED"===M&&r(W.last_text,["var","let","const"])&&"TK_WORD"===B.type||"TK_RESERVED"===M&&"do"===W.last_text||"TK_RESERVED"===M&&r(W.last_text,q)&&!B.wanted_newline||"TK_RESERVED"===M&&"else"===W.last_text&&("TK_RESERVED"!==B.type||"if"!==B.text||B.comments_before.length)||"TK_END_EXPR"===M&&(X.mode===h.ForInitializer||X.mode===h.Conditional)||"TK_WORD"===M&&W.mode===h.BlockStatement&&!W.in_case&&"--"!==B.text&&"++"!==B.text&&"function"!==I&&"TK_WORD"!==B.type&&"TK_RESERVED"!==B.type||W.mode===h.ObjectLiteral&&(":"===W.last_text&&0===W.ternary_depth||"TK_RESERVED"===M&&r(W.last_text,["get","set"])))&&(v(h.Statement),Q.unindent_chained_methods||m(),f(B,!0),S()||E("TK_RESERVED"===B.type&&r(B.text,["do","for","if","while"])),!0)}function g(e,t){for(var n=0;n=$.length?null:$[t]}function N(){if("TK_RESERVED"===B.type&&(r(B.text,["set","get"])&&W.mode!==h.ObjectLiteral?B.type="TK_WORD":r(B.text,["as","from"])&&!W.import_block?B.type="TK_WORD":W.mode===h.ObjectLiteral&&":"===C(1).text&&(B.type="TK_WORD")),y()?"TK_RESERVED"===M&&r(W.last_text,["var","let","const"])&&"TK_WORD"===B.type&&(W.declaration_statement=!0):!B.wanted_newline||w(W.mode)||"TK_OPERATOR"===M&&"--"!==W.last_text&&"++"!==W.last_text||"TK_EQUALS"===M||!Q.preserve_newlines&&"TK_RESERVED"===M&&r(W.last_text,["var","let","const","set","get"])?f(B):(f(B),x()),W.do_block&&!W.do_while){if("TK_RESERVED"===B.type&&"while"===B.text)return L.space_before_token=!0,K(),L.space_before_token=!0,void(W.do_while=!0);x(),W.do_block=!1}if(W.if_block)if(W.else_block||"TK_RESERVED"!==B.type||"else"!==B.text){for(;W.mode===h.Statement;)O();W.if_block=!1,W.else_block=!1}else W.else_block=!0;return"TK_RESERVED"===B.type&&("case"===B.text||"default"===B.text&&W.in_case_statement)?(x(),(W.case_body||Q.jslint_happy)&&(b(),W.case_body=!1),K(),W.in_case=!0,void(W.in_case_statement=!0)):("TK_COMMA"!==M&&"TK_START_EXPR"!==M&&"TK_EQUALS"!==M&&"TK_OPERATOR"!==M||S()||E(),"TK_RESERVED"===B.type&&"function"===B.text?((r(W.last_text,["}",";"])||L.just_added_newline()&&!r(W.last_text,["(","[","{",":","=",","])&&"TK_OPERATOR"!==M)&&(L.just_added_blankline()||B.comments_before.length||(x(),x(!0))),"TK_RESERVED"===M||"TK_WORD"===M?"TK_RESERVED"===M&&(r(W.last_text,["get","set","new","export","async"])||r(W.last_text,q))?L.space_before_token=!0:"TK_RESERVED"===M&&"default"===W.last_text&&"export"===I?L.space_before_token=!0:x():"TK_OPERATOR"===M||"="===W.last_text?L.space_before_token=!0:(W.multiline_frame||!w(W.mode)&&!k(W.mode))&&x(),K(),void(W.last_word=B.text)):(F="NONE","TK_END_BLOCK"===M?X.inline_frame?F="SPACE":"TK_RESERVED"===B.type&&r(B.text,["else","catch","finally","from"])?"expand"===Q.brace_style||"end-expand"===Q.brace_style||"none"===Q.brace_style&&B.wanted_newline?F="NEWLINE":(F="SPACE",L.space_before_token=!0):F="NEWLINE":"TK_SEMICOLON"===M&&W.mode===h.BlockStatement?F="NEWLINE":"TK_SEMICOLON"===M&&w(W.mode)?F="SPACE":"TK_STRING"===M?F="NEWLINE":"TK_RESERVED"===M||"TK_WORD"===M||"*"===W.last_text&&(r(I,["function","yield"])||W.mode===h.ObjectLiteral&&r(I,["{",","]))?F="SPACE":"TK_START_BLOCK"===M?F=W.inline_frame?"SPACE":"NEWLINE":"TK_END_EXPR"===M&&(L.space_before_token=!0,F="NEWLINE"),"TK_RESERVED"===B.type&&r(B.text,P.line_starters)&&")"!==W.last_text&&(F=W.inline_frame||"else"===W.last_text||"export"===W.last_text?"SPACE":"NEWLINE"),"TK_RESERVED"===B.type&&r(B.text,["else","catch","finally"])?("TK_END_BLOCK"!==M||X.mode!==h.BlockStatement||"expand"===Q.brace_style||"end-expand"===Q.brace_style||"none"===Q.brace_style&&B.wanted_newline)&&!W.inline_frame?x():(L.trim(!0),"}"!==L.current_line.last()&&x(),L.space_before_token=!0):"NEWLINE"===F?"TK_RESERVED"===M&&A(W.last_text)?L.space_before_token=!0:"TK_END_EXPR"!==M?"TK_START_EXPR"===M&&"TK_RESERVED"===B.type&&r(B.text,["var","let","const"])||":"===W.last_text||("TK_RESERVED"===B.type&&"if"===B.text&&"else"===W.last_text?L.space_before_token=!0:x()):"TK_RESERVED"===B.type&&r(B.text,P.line_starters)&&")"!==W.last_text&&x():W.multiline_frame&&k(W.mode)&&","===W.last_text&&"}"===I?x():"SPACE"===F&&(L.space_before_token=!0),K(),W.last_word=B.text,void("TK_RESERVED"===B.type&&("do"===B.text?W.do_block=!0:"if"===B.text?W.if_block=!0:"import"===B.text?W.import_block=!0:W.import_block&&"TK_RESERVED"===B.type&&"from"===B.text&&(W.import_block=!1)))))}var L,V,P,B,M,I,j,W,X,z,F,U,Q,$=[],Z="";U={TK_START_EXPR:function(){y()||f(B);var e=h.Expression;if("["===B.text){if("TK_WORD"===M||")"===W.last_text)return"TK_RESERVED"===M&&r(W.last_text,P.line_starters)&&(L.space_before_token=!0),v(e),K(),m(),void(Q.space_in_paren&&(L.space_before_token=!0));e=h.ArrayLiteral,k(W.mode)&&("["!==W.last_text&&(","!==W.last_text||"]"!==I&&"}"!==I)||Q.keep_array_indentation||x())}else"TK_RESERVED"===M&&"for"===W.last_text?e=h.ForInitializer:"TK_RESERVED"===M&&r(W.last_text,["if","while"])&&(e=h.Conditional);";"===W.last_text||"TK_START_BLOCK"===M?x():"TK_END_EXPR"===M||"TK_START_EXPR"===M||"TK_END_BLOCK"===M||"."===W.last_text?E(B.wanted_newline):"TK_RESERVED"===M&&"("===B.text||"TK_WORD"===M||"TK_OPERATOR"===M?"TK_RESERVED"===M&&("function"===W.last_word||"typeof"===W.last_word)||"*"===W.last_text&&(r(I,["function","yield"])||W.mode===h.ObjectLiteral&&r(I,["{",","]))?Q.space_after_anon_function&&(L.space_before_token=!0):"TK_RESERVED"!==M||!r(W.last_text,P.line_starters)&&"catch"!==W.last_text||Q.space_before_conditional&&(L.space_before_token=!0):L.space_before_token=!0,"("===B.text&&"TK_RESERVED"===M&&"await"===W.last_word&&(L.space_before_token=!0),"("===B.text&&("TK_EQUALS"!==M&&"TK_OPERATOR"!==M||S()||E()),"("===B.text&&"TK_WORD"!==M&&"TK_RESERVED"!==M&&E(),v(e),K(),Q.space_in_paren&&(L.space_before_token=!0),m()},TK_END_EXPR:function(){for(;W.mode===h.Statement;)O();f(B),W.multiline_frame&&E("]"===B.text&&k(W.mode)&&!Q.keep_array_indentation),Q.space_in_paren&&("TK_START_EXPR"!==M||Q.space_in_empty_paren?L.space_before_token=!0:(L.trim(),L.space_before_token=!1)),"]"===B.text&&Q.keep_array_indentation?(K(),O()):(O(),K()),i(L,X),W.do_while&&X.mode===h.Conditional&&(X.mode=h.Expression,W.do_block=!1,W.do_while=!1)},TK_START_BLOCK:function(){f(B);var e=C(1),t=C(2);v(t&&(r(t.text,[":",","])&&r(e.type,["TK_STRING","TK_WORD","TK_RESERVED"])||r(e.text,["get","set","..."])&&r(t.type,["TK_WORD","TK_RESERVED"]))?r(I,["class","interface"])?h.BlockStatement:h.ObjectLiteral:"TK_OPERATOR"===M&&"=>"===W.last_text?h.BlockStatement:r(M,["TK_EQUALS","TK_START_EXPR","TK_COMMA","TK_OPERATOR"])||"TK_RESERVED"===M&&r(W.last_text,["return","throw","import","default"])?h.ObjectLiteral:h.BlockStatement);var n=!e.comments_before.length&&"}"===e.text&&"function"===W.last_word&&"TK_END_EXPR"===M;if(Q.brace_preserve_inline){var i=0,_=null;W.inline_frame=!0;do{if(i+=1,(_=C(i)).wanted_newline){W.inline_frame=!1;break}}while("TK_EOF"!==_.type&&("TK_END_BLOCK"!==_.type||_.opened!==B))}("expand"===Q.brace_style||"none"===Q.brace_style&&B.wanted_newline)&&!W.inline_frame?"TK_OPERATOR"!==M&&(n||"TK_EQUALS"===M||"TK_RESERVED"===M&&A(W.last_text)&&"else"!==W.last_text)?L.space_before_token=!0:x(!1,!0):(!k(X.mode)||"TK_START_EXPR"!==M&&"TK_COMMA"!==M||(("TK_COMMA"===M||Q.space_in_paren)&&(L.space_before_token=!0),("TK_COMMA"===M||"TK_START_EXPR"===M&&W.inline_frame)&&(E(),X.multiline_frame=X.multiline_frame||W.multiline_frame,W.multiline_frame=!1)),"TK_OPERATOR"!==M&&"TK_START_EXPR"!==M&&("TK_START_BLOCK"!==M||W.inline_frame?L.space_before_token=!0:x())),K(),m()},TK_END_BLOCK:function(){for(f(B);W.mode===h.Statement;)O();var e="TK_START_BLOCK"===M;W.inline_frame&&!e?L.space_before_token=!0:"expand"===Q.brace_style?e||x():e||(k(W.mode)&&Q.keep_array_indentation?(Q.keep_array_indentation=!1,x(),Q.keep_array_indentation=!0):x()),O(),K()},TK_WORD:N,TK_RESERVED:N,TK_SEMICOLON:function(){y()?L.space_before_token=!1:f(B);for(var e=C(1);!(W.mode!==h.Statement||W.if_block&&e&&"TK_RESERVED"===e.type&&"else"===e.text||W.do_block);)O();W.import_block&&(W.import_block=!1),K()},TK_STRING:function(){y()?L.space_before_token=!0:(f(B),"TK_RESERVED"===M||"TK_WORD"===M||W.inline_frame?L.space_before_token=!0:"TK_COMMA"===M||"TK_START_EXPR"===M||"TK_EQUALS"===M||"TK_OPERATOR"===M?S()||E():x()),K()},TK_EQUALS:function(){y()||f(B),W.declaration_statement&&(W.declaration_assignment=!0),L.space_before_token=!0,K(),L.space_before_token=!0},TK_OPERATOR:function(){var e="*"===B.text&&("TK_RESERVED"===M&&r(W.last_text,["function","yield"])||r(M,["TK_START_BLOCK","TK_COMMA","TK_END_BLOCK","TK_SEMICOLON"])),t=r(B.text,["-","+"])&&(r(M,["TK_START_BLOCK","TK_START_EXPR","TK_EQUALS","TK_OPERATOR"])||r(W.last_text,P.line_starters)||","===W.last_text);if(y()||f(B,!e),"TK_RESERVED"===M&&A(W.last_text))return L.space_before_token=!0,void K();if("*"!==B.text||"TK_DOT"!==M)if("::"!==B.text){if("TK_OPERATOR"===M&&r(Q.operator_position,d)&&E(),":"===B.text&&W.in_case)return W.case_body=!0,m(),K(),x(),void(W.in_case=!1);var n=!0,i=!0,_=!1;if(":"===B.text?0===W.ternary_depth?n=!1:(W.ternary_depth-=1,_=!0):"?"===B.text&&(W.ternary_depth+=1),!t&&!e&&Q.preserve_newlines&&r(B.text,P.positionable_operators)){var a=":"===B.text,o=a&&_,s=a&&!_;switch(Q.operator_position){case u.before_newline:return L.space_before_token=!s,K(),a&&!o||E(),void(L.space_before_token=!0);case u.after_newline:return L.space_before_token=!0,!a||o?C(1).wanted_newline?x(!1,!0):E():L.space_before_token=!1,K(),void(L.space_before_token=!0);case u.preserve_newline:return s||E(),n=!(L.just_added_newline()||s),L.space_before_token=n,K(),void(L.space_before_token=!0)}}if(e){E(),n=!1;var l=C(1);i=l&&r(l.type,["TK_WORD","TK_RESERVED"])}else"..."===B.text?(E(),n="TK_START_BLOCK"===M,i=!1):(r(B.text,["--","++","!","~"])||t)&&(n=!1,i=!1,!B.wanted_newline||"--"!==B.text&&"++"!==B.text||x(!1,!0),";"===W.last_text&&w(W.mode)&&(n=!0),"TK_RESERVED"===M?n=!0:"TK_END_EXPR"===M?n=!("]"===W.last_text&&("--"===B.text||"++"===B.text)):"TK_OPERATOR"===M&&(n=r(B.text,["--","-","++","+"])&&r(W.last_text,["--","-","++","+"]),r(B.text,["+","-"])&&r(W.last_text,["--","++"])&&(i=!0)),(W.mode!==h.BlockStatement||W.inline_frame)&&W.mode!==h.Statement||"{"!==W.last_text&&";"!==W.last_text||x());L.space_before_token=L.space_before_token||n,K(),L.space_before_token=i}else K();else K()},TK_COMMA:function(){f(B,!0),K(),L.space_before_token=!0,W.declaration_statement?(w(W.parent.mode)&&(W.declaration_assignment=!1),W.declaration_assignment?(W.declaration_assignment=!1,x(!1,!0)):Q.comma_first&&E()):W.mode===h.ObjectLiteral||W.mode===h.Statement&&W.parent.mode===h.ObjectLiteral?(W.mode===h.Statement&&O(),W.inline_frame||x()):Q.comma_first&&E()},TK_BLOCK_COMMENT:function(e){if(L.raw)return L.add_raw_token(B),void(B.directives&&"end"===B.directives.preserve&&(L.raw=Q.test_output_raw));if(B.directives)return x(!1,e),K(),"start"===B.directives.preserve&&(L.raw=!0),void x(!1,!0);if(!l.newline.test(B.text)&&!B.wanted_newline)return L.space_before_token=!0,K(),void(L.space_before_token=!0);var t,n=T(B.text),i=!1,r=!1,_=B.whitespace_before,o=_.length;for(x(!1,e),n.length>1&&(i=g(n.slice(1),"*"),r=D(n.slice(1),_)),K(n[0]),t=1;to?K(n[t].substring(o)):L.add_token(n[t]);x(!1,e)},TK_COMMENT:function(e){B.wanted_newline?x(!1,e):L.trim(!0),L.space_before_token=!0,K(),x(!1,e)},TK_DOT:function(){y()||f(B,!0),"TK_RESERVED"===M&&A(W.last_text)?L.space_before_token=!0:E(")"===W.last_text&&Q.break_chained_methods),K()},TK_UNKNOWN:function(e){K(),"\n"===B.text[B.text.length-1]&&x(!1,e)},TK_EOF:function(){for(;W.mode===h.Statement;)O();f(B)}},t=s(t=t||{},"js"),Q={},"expand-strict"===t.brace_style?t.brace_style="expand":"collapse-preserve-inline"===t.brace_style?t.brace_style="collapse,preserve-inline":void 0!==t.braces_on_own_line?t.brace_style=t.braces_on_own_line?"expand":"collapse":t.brace_style||(t.brace_style="collapse");var G=t.brace_style.split(/[^a-zA-Z0-9_\-]+/);for(Q.brace_style=G[0],Q.brace_preserve_inline=!!G[1]&&G[1],Q.indent_size=t.indent_size?parseInt(t.indent_size,10):4,Q.indent_char=t.indent_char?t.indent_char:" ",Q.eol=t.eol?t.eol:"auto",Q.preserve_newlines=void 0===t.preserve_newlines||t.preserve_newlines,Q.unindent_chained_methods=void 0!==t.unindent_chained_methods&&t.unindent_chained_methods,Q.break_chained_methods=void 0!==t.break_chained_methods&&t.break_chained_methods,Q.max_preserve_newlines=void 0===t.max_preserve_newlines?0:parseInt(t.max_preserve_newlines,10),Q.space_in_paren=void 0!==t.space_in_paren&&t.space_in_paren,Q.space_in_empty_paren=void 0!==t.space_in_empty_paren&&t.space_in_empty_paren,Q.jslint_happy=void 0!==t.jslint_happy&&t.jslint_happy,Q.space_after_anon_function=void 0!==t.space_after_anon_function&&t.space_after_anon_function,Q.keep_array_indentation=void 0!==t.keep_array_indentation&&t.keep_array_indentation,Q.space_before_conditional=void 0===t.space_before_conditional||t.space_before_conditional,Q.unescape_strings=void 0!==t.unescape_strings&&t.unescape_strings,Q.wrap_line_length=void 0===t.wrap_line_length?0:parseInt(t.wrap_line_length,10),Q.e4x=void 0!==t.e4x&&t.e4x,Q.end_with_newline=void 0!==t.end_with_newline&&t.end_with_newline,Q.comma_first=void 0!==t.comma_first&&t.comma_first,Q.operator_position=o(t.operator_position),Q.test_output_raw=void 0!==t.test_output_raw&&t.test_output_raw,Q.jslint_happy&&(Q.space_after_anon_function=!0),t.indent_with_tabs&&(Q.indent_char="\t",Q.indent_size=1),"auto"===Q.eol&&(Q.eol="\n",e&&l.lineBreak.test(e||"")&&(Q.eol=e.match(l.lineBreak)[0])),Q.eol=Q.eol.replace(/\\r/,"\r").replace(/\\n/,"\n"),j="";Q.indent_size>0;)j+=Q.indent_char,Q.indent_size-=1;var Y=0;if(e&&e.length){for(;" "===e.charAt(Y)||"\t"===e.charAt(Y);)Y+=1;Z=e.substring(0,Y),e=e.substring(Y)}M="TK_START_BLOCK",I="",(L=new c(j,Z)).raw=Q.test_output_raw,z=[],v(h.BlockStatement),this.beautify=function(){for(P=new p(e,Q,j),$=P.tokenize(),V=0,B=C();B;)U[B.type](),I=W.last_text,M=B.type,W.last_text=B.text,V+=1,B=C();return L.get_code(Q.end_with_newline,Q.eol)};var q=["break","continue","return","throw","yield"]}},function(e,t){e.exports.InputScanner=function(e){var t=e,n=t.length,i=0;this.back=function(){i-=1},this.hasNext=function(){return i=0&&e=0&&e0&&(n-=1,t-=e.indent_length)},this.trim=function(){for(;" "===this.last();)i.pop(),t-=1;r=0===i.length},this.toString=function(){var t="";return this._empty||(n>=0&&(t=e.indent_cache[n]),t+=i.join("")),t}}e.exports.Output=function(e,t){t=t||"",this.indent_cache=[t],this.baseIndentLength=t.length,this.indent_length=e.length,this.raw=!1;var i=[];this.baseIndentString=t,this.indent_string=e,this.previous_line=null,this.current_line=null,this.space_before_token=!1,this.add_outputline=function(){this.previous_line=this.current_line,this.current_line=new n(this),i.push(this.current_line)},this.add_outputline(),this.get_line_number=function(){return i.length},this.add_new_line=function(e){return!(1===this.get_line_number()&&this.just_added_newline()||!e&&this.just_added_newline()||(this.raw||this.add_outputline(),0))},this.get_code=function(e,t){var n=i.join("\n").replace(/[\r\n\t ]+$/,"");return e&&(n+="\n"),"\n"!==t&&(n=n.replace(/[\n]/g,t)),n},this.set_indent=function(e){if(i.length>1){for(;e>=this.indent_cache.length;)this.indent_cache.push(this.indent_cache[this.indent_cache.length-1]+this.indent_string);return this.current_line.set_indent(e),!0}return this.current_line.set_indent(0),!1},this.add_raw_token=function(e){for(var t=0;t1&&this.current_line.is_empty();)i.pop(),this.current_line=i[i.length-1],this.current_line.trim();this.previous_line=i.length>1?i[i.length-2]:null},this.just_added_newline=function(){return this.current_line.is_empty()},this.just_added_blankline=function(){return!!this.just_added_newline()&&(1===i.length||i[i.length-2].is_empty())}}},function(e,t){e.exports.Token=function(e,t,n,i,r){this.type=e,this.text=t,this.comments_before=[],this.comments_after=[],this.newlines=n||0,this.wanted_newline=n>0,this.whitespace_before=i||"",this.parent=r||null,this.opened=null,this.directives=null}},function(e,t,n){var i=n(1).Beautifier;e.exports=function(e,t){return new i(e,t).beautify()}},function(e,t,n){function i(e){return e.replace(/^\s+|\s+$/g,"")}function r(e,t){for(var n=0;n/g;if("`"===s||"'"===s||'"'===s||("/"===s||t.e4x&&"<"===s&&K.test(N,-1))&&("TK_RESERVED"===k.type&&r(k.text,["return","case","throw","else","do","typeof","yield"])||"TK_END_EXPR"===k.type&&")"===k.text&&k.parent&&"TK_RESERVED"===k.parent.type&&r(k.parent.text,["if","while","for"])||r(k.type,["TK_COMMENT","TK_START_EXPR","TK_START_BLOCK","TK_END_BLOCK","TK_OPERATOR","TK_EQUALS","TK_EOF","TK_SEMICOLON","TK_COMMA"]))){var L=s,V=!1,P=!1;if(e=s,"/"===L)for(var B=!1;K.hasNext()&&(V||B||K.peek()!==L)&&!K.testChar(o.newline);)e+=K.peek(),V?V=!1:(V="\\"===K.peek(),"["===K.peek()?B=!0:"]"===K.peek()&&(B=!1)),K.next();else if(t.e4x&&"<"===L){var M=/[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{[\s\S]+?}))*\s*(\/?)\s*>/g;K.back();var I="",j=K.match(N);if(j){for(var W=j[2].replace(/^{\s+/,"{").replace(/\s+}$/,"}"),X=0===W.indexOf("{"),z=0;j;){var F=!!j[1],U=j[2];if(!(j[j.length-1]||"![CDATA["===U.slice(0,8))&&(U===W||X&&U.replace(/^{\s+/,"{").replace(/\s+}$/,"}"))&&(F?--z:++z),I+=j[0],z<=0)break;j=K.match(M)}return j||(I+=K.match(/[\s\S]*/g)[0]),I=I.replace(o.allLineBreaks,"\n"),[I,"TK_STRING"]}}else{var Q=function(t,n,i){for(var r;K.hasNext()&&(r=K.peek(),V||r!==t&&(n||!o.newline.test(r)));)(V||n)&&o.newline.test(r)?("\r"===r&&"\n"===K.peek(1)&&(K.next(),r=K.peek()),e+="\n"):e+=r,V?("x"!==r&&"u"!==r||(P=!0),V=!1):V="\\"===r,K.next(),i&&-1!==e.indexOf(i,e.length-i.length)&&("`"===t?Q("}",n,"`"):Q("`",n,"${"),K.hasNext()&&(e+=K.next()))};"`"===L?Q("`",!0,"${"):Q(L)}if(P&&t.unescape_strings&&(e=l(e)),K.peek()===L&&(e+=L,K.next(),"/"===L))for(;K.hasNext()&&o.isIdentifierStart(K.peekCharCode());)e+=K.next();return[e,"TK_STRING"]}if("#"===s){if(0===R.length&&"!"===K.peek()){for(e=s;K.hasNext()&&"\n"!==s;)s=K.next(),e+=s;return[i(e)+"\n","TK_UNKNOWN"]}var $="#";if(K.hasNext()&&K.testChar(p)){do{$+=s=K.next()}while(K.hasNext()&&"#"!==s&&"="!==s);return"#"===s||("["===K.peek()&&"]"===K.peek(1)?($+="[]",K.next(),K.next()):"{"===K.peek()&&"}"===K.peek(1)&&($+="{}",K.next(),K.next())),[$,"TK_WORD"]}}if("<"===s&&("?"===K.peek()||"%"===K.peek())){K.back();var Z=K.match(S);if(Z)return s=Z[0],s=s.replace(o.allLineBreaks,"\n"),[s,"TK_STRING"]}if("<"===s&&K.match(/\!--/g)){for(s="\x3c!--";K.hasNext()&&!K.testChar(o.newline);)s+=K.next();return x=!0,[s,"TK_COMMENT"]}if("-"===s&&x&&K.match(/->/g))return x=!1,["--\x3e","TK_COMMENT"];if("."===s)return"."===K.peek()&&"."===K.peek(1)?(s+=K.next()+K.next(),[s,"TK_OPERATOR"]):[s,"TK_DOT"];if(r(s,h)){for(;K.hasNext()&&r(s+K.peek(),h)&&(s+=K.next(),K.hasNext()););return","===s?[s,"TK_COMMA"]:"="===s?[s,"TK_EQUALS"]:[s,"TK_OPERATOR"]}return[s,"TK_UNKNOWN"]}function l(e){for(var t="",n=0,i=new _(e),r=null;i.hasNext();)if((r=i.match(/([\s]|[^\\]|\\\\)+/g))&&(t+=r[0]),"\\"===i.peek()){if(i.next(),"x"===i.peek())r=i.match(/x([0-9A-Fa-f]{2})/g);else{if("u"!==i.peek()){t+="\\",i.hasNext()&&(t+=i.next());continue}r=i.match(/u([0-9A-Fa-f]{4})/g)}if(!r)return e;if((n=parseInt(r[1],16))>126&&n<=255&&0===r[0].indexOf("x"))return e;if(n>=0&&n<32){t+="\\"+r[0];continue}t+=34===n||39===n||92===n?"\\"+String.fromCharCode(n):String.fromCharCode(n)}return t}var c="\n\r\t ".split(""),p=/[0-9]/,f=/[01]/,u=/[01234567]/,d=/[0123456789abcdefABCDEF]/;this.positionable_operators="!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||".split(" ");var h=this.positionable_operators.concat("! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...".split(" "));this.line_starters="continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export".split(",");var T,E,x,R,K,m=this.line_starters.concat(["do","in","of","else","get","set","new","catch","finally","typeof","yield","async","await","from","as"]),b=/([\s\S]*?)((?:\*\/)|$)/g,v=/([^\n\r\u2028\u2029]*)/g,k=/\/\* beautify( \w+[:]\w+)+ \*\//g,w=/ (\w+)[:](\w+)/g,O=/([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g,S=/((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g;this.tokenize=function(){K=new _(e),x=!1,R=[];for(var t,n,i,r=null,o=[],l=[];!n||"TK_EOF"!==n.type;){for(i=s(),t=new a(i[1],i[0],T,E);"TK_COMMENT"===t.type||"TK_BLOCK_COMMENT"===t.type||"TK_UNKNOWN"===t.type;)"TK_BLOCK_COMMENT"===t.type&&(t.directives=i[2]),l.push(t),i=s(),t=new a(i[1],i[0],T,E);l.length&&(t.comments_before=l,l=[]),"TK_START_BLOCK"===t.type||"TK_START_EXPR"===t.type?(t.parent=n,o.push(r),r=t):("TK_END_BLOCK"===t.type||"TK_END_EXPR"===t.type)&&r&&("]"===t.text&&"["===r.text||")"===t.text&&"("===r.text||"}"===t.text&&"{"===r.text)&&(t.parent=r.parent,t.opened=r,r=o.pop()),R.push(t),n=t}return R}}}]);"function"==typeof define&&define.amd?define([],function(){return{js_beautify:e}}):"undefined"!=typeof exports?exports.js_beautify=e:"undefined"!=typeof window?window.js_beautify=e:"undefined"!=typeof global&&(global.js_beautify=e)}(); \ No newline at end of file diff --git a/app/src/main/assets/editor/codemirror/.bower.json b/app/src/main/assets/editor/codemirror/.bower.json deleted file mode 100644 index 0b76ad07..00000000 --- a/app/src/main/assets/editor/codemirror/.bower.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "codemirror", - "main": [ - "lib/codemirror.js", - "lib/codemirror.css" - ], - "ignore": [ - "**/.*", - "node_modules", - "components", - "bin", - "demo", - "doc", - "test", - "index.html", - "package.json", - "mode/*/*test.js", - "mode/*/*.html" - ], - "homepage": "https://github.com/components/codemirror", - "version": "5.30.0", - "_release": "5.30.0", - "_resolution": { - "type": "version", - "tag": "5.30.0", - "commit": "13706af84d4be6a880af86faaf42c010f4d0861f" - }, - "_source": "https://github.com/components/codemirror.git", - "_target": "^5.30.0", - "_originalSource": "codemirror", - "_direct": true -} \ No newline at end of file diff --git a/app/src/main/assets/editor/codemirror/AUTHORS b/app/src/main/assets/editor/codemirror/AUTHORS deleted file mode 100644 index 12643570..00000000 --- a/app/src/main/assets/editor/codemirror/AUTHORS +++ /dev/null @@ -1,689 +0,0 @@ -List of CodeMirror contributors. Updated before every release. - -4r2r -Aaron Brooks -Abdelouahab -Abe Fettig -Adam Ahmed -Adam King -Adam Particka -adanlobato -Adán Lobato -Adrian Aichner -Adrian Heine -Adrien Bertrand -aeroson -Ahmad Amireh -Ahmad M. Zawawi -ahoward -Akeksandr Motsjonov -Alasdair Smith -Alberto González Palomo -Alberto Pose -Albert Xing -Alexander Pavlov -Alexander Schepanovski -Alexander Shvets -Alexander Solovyov -Alexandre Bique -alexey-k -Alex Piggott -Aliaksei Chapyzhenka -Allen Sarkisyan -Ami Fischman -Amin Shali -Amin Ullah Khan -amshali@google.com -Amsul -amuntean -Amy -Ananya Sen -anaran -AndersMad -Anders Nawroth -Anderson Mesquita -Anders Wåglund -Andrea G -Andreas Reischuck -Andres Taylor -Andre von Houck -Andrew Cheng -Andrey Fedorov -Andrey Klyuchnikov -Andrey Lushnikov -Andrey Shchekin -Andy Joslin -Andy Kimball -Andy Li -Angelo -angelozerr -angelo.zerr@gmail.com -Ankit -Ankit Ahuja -Ansel Santosa -Anthony Dugois -anthonygego -Anthony Gégo -Anthony Grimes -Anton Kovalyov -Apollo Zhu -AQNOUCH Mohammed -Aram Shatakhtsyan -areos -Arnab Bose -Arsène von Wyss -Arthur Müller -Arun Narasani -as3boyan -asolove -atelierbram -AtomicPages LLC -Atul Bhouraskar -Aurelian Oancea -Axel Lewenhaupt -Barret Rennie -Basarat Ali Syed -Bastian Müller -belhaj -Bem Jones-Bey -benbro -Beni Cherniavsky-Paskin -Benjamin DeCoste -Benjamin Young -Ben Keen -Ben Miller -Ben Mosher -Bernhard Sirlinger -Bert Chang -Bharad -BigBlueHat -Billy Moon -binny -B Krishna Chaitanya -Blaine G -blukat29 -boomyjee -borawjm -Brad Metcalf -Brandon Frohs -Brandon Wamboldt -Brett Zamir -Brian Grinstead -Brian Sletten -brrd -Bruce Mitchener -Bryan Massoth -Caitlin Potter -Calin Barbat -callodacity -Camilo Roca -Chad Jolly -Chandra Sekhar Pydi -Charles Skelton -Cheah Chu Yeow -Chris Coyier -Chris Ford -Chris Granger -Chris Houseknecht -Chris Lohfink -Chris Morgan -Chris Smith -Christian Gruen -Christian Oyarzun -Christian Petrov -Christopher Brown -Christopher Kramer -Christopher Mitchell -Christopher Pfohl -Chunliang Lyu -ciaranj -CodeAnimal -CodeBitt -coderaiser -Cole R Lawrence -ComFreek -Curtis Gagliardi -dagsta -daines -Dale Jung -Dan Bentley -Dan Heberden -Daniel, Dao Quang Minh -Daniele Di Sarli -Daniel Faust -Daniel Huigens -Daniel Kesler -Daniel KJ -Daniel Neel -Daniel Parnell -Daniel Thwaites -Danila Malyutin -Danny Yoo -darealshinji -Darius Roberts -Dave Brondsema -Dave Myers -David Barnett -David H. Bronke -David Mignot -David Pathakjee -David Vázquez -David Whittington -deebugger -Deep Thought -Devin Abbott -Devon Carew -Dick Choi -dignifiedquire -Dimage Sapelkin -Dmitry Kiselyov -domagoj412 -Dominator008 -Domizio Demichelis -Doug Wikle -Drew Bratcher -Drew Hintz -Drew Khoury -Drini Cami -Dror BG -duralog -dwelle -eborden -edsharp -ekhaled -Elisée -Emmanuel Schanzer -Enam Mijbah Noor -Eric Allam -Erik Welander -eustas -Fabien Dubosson -Fabien O'Carroll -Fabio Zendhi Nagao -Faiza Alsaied -Fauntleroy -fbuchinger -feizhang365 -Felipe Lalanne -Felix Raab -ficristo -Filip Noetzel -Filip Stollár -flack -Florian Felten -ForbesLindesay -Forbes Lindesay -Ford_Lawnmower -Forrest Oliphant -Franco Catena -Frank Wiegand -Fredrik Borg -Gabriel Gheorghian -Gabriel Horner -Gabriel Nahmias -galambalazs -Gary Sheng -Gautam Mehta -Gavin Douglas -gekkoe -Geordie Hall -George Stephanis -geowarin -Gerard Braad -Gergely Hegykozi -Giovanni Calò -Glebov Boris -Glenn Jorde -Glenn Ruehle -Golevka -Google Inc. -Gordon Smith -Grant Skinner -greengiant -Gregory Koberger -Grzegorz Mazur -Guillaume Massé -Guillaume Massé -guraga -Gustavo Rodrigues -Hakan Tunc -Hans Engel -Hardest -Harshvardhan Gupta -Hasan Karahan -Hector Oswaldo Caballero -Hendrik Wallbaum -Herculano Campos -Hiroyuki Makino -hitsthings -Hocdoc -Hugues Malphettes -Ian Beck -Ian Davies -Ian Dickinson -Ian Wehrman -Ian Wetherbee -Ice White -ICHIKAWA, Yuji -idleberg -ilvalle -Ingo Richter -Irakli Gozalishvili -Ivan Kurnosov -Ivoah -Jacob Lee -Jaimin -Jake Peyser -Jakob Miland -Jakub Vrana -Jakub Vrána -James Campos -James Howard -James Thorne -Jamie Hill -Jamie Morris -Jan Jongboom -jankeromnes -Jan Keromnes -Jan Odvarko -Jan Schär -Jan T. Sott -Jared Dean -Jared Forsyth -Jared Jacobs -Jason -Jason Barnabe -Jason Grout -Jason Johnston -Jason San Jose -Jason Siefken -Jaydeep Solanki -Jean Boussier -Jeff Blaisdell -Jeff Hanke -Jeff Jenkins -jeffkenton -Jeff Pickhardt -jem (graphite) -Jeremy Parmenter -Jim -Jim Avery -JobJob -jochenberger -Jochen Berger -Joel Einbinder -joelpinheiro -Johan Ask -John Connor -John-David Dalton -John Engler -John Lees-Miller -John Snelson -John Van Der Loo -Jon Ander Peñalba -Jonas Döbertin -Jonas Helfer -Jonathan Malmaud -Jon Gacnik -jongalloway -Jon Malmaud -Jon Sangster -Joost-Wim Boekesteijn -Joseph Pecoraro -Josh Barnes -Josh Cohen -Josh Soref -Joshua Newman -Josh Watzman -jots -jsoojeon -ju1ius -Juan Benavides Romero -Jucovschi Constantin -Juho Vuori -Julien Rebetez -Justin Andresen -Justin Hileman -jwallers@gmail.com -kaniga -karevn -Kayur Patel -Kazuhito Hokamura -Ken Newman -ken restivo -Ken Rockot -Kevin Earls -Kevin Kwok -Kevin Muret -Kevin Sawicki -Kevin Ushey -Klaus Silveira -Koh Zi Han, Cliff -komakino -Konstantin Lopuhin -koops -Kris Ciccarello -ks-ifware -kubelsmieci -KwanEsq -Kyle Kelley -KyleMcNutt -Lanfei -Lanny -Laszlo Vidacs -leaf corcoran -Leonid Khachaturov -Leon Sorokin -Leonya Khachaturov -Liam Newman -Libo Cannici -LloydMilligan -LM -lochel -Lorenzo Stoakes -Louis Mauchet -Luca Fabbri -Luciano Longo -Lu Fangjian -Luke Browning -Luke Granger-Brown -Luke Stagner -lynschinzer -M1cha -Madhura Jayaratne -Maksim Lin -Maksym Taran -Malay Majithia -Manideep -Manuel Rego Casasnovas -Marat Dreizin -Marcel Gerber -Marcelo Camargo -Marco Aurélio -Marco Munizaga -Marcus Bointon -Marek Rudnicki -Marijn Haverbeke -Mário Gonçalves -Mario Pietsch -Mark Anderson -Mark Lentczner -Marko Bonaci -Mark Peace -Markus Bordihn -Martin Balek -Martín Gaitán -Martin Hasoň -Martin Hunt -Martin Laine -Martin Zagora -Mason Malone -Mateusz Paprocki -Mathias Bynens -mats cronqvist -Matt Gaide -Matthew Bauer -Matthew Beale -matthewhayes -Matthew Rathbone -Matthias Bussonnier -Matthias BUSSONNIER -Matt McDonald -Matt Pass -Matt Sacks -mauricio -Maximilian Hils -Maxim Kraev -Max Kirsch -Max Schaefer -Max Xiantu -mbarkhau -McBrainy -mce2 -melpon -Metatheos -Micah Dubinko -Michael -Michael Goderbauer -Michael Grey -Michael Kaminsky -Michael Lehenbauer -Michael Walker -Michael Zhou -Michal Čihař -Michal Dorner -Mighty Guava -Miguel Castillo -mihailik -Mike -Mike Brevoort -Mike Diaz -Mike Ivanov -Mike Kadin -Mike Kobit -MinRK -Miraculix87 -misfo -mkaminsky11 -mloginov -Moritz Schwörer -Moshe Wajnberg -mps -ms -mtaran-google -Mu-An Chiou -mzabuawala -Narciso Jaramillo -Nathan Williams -ndr -nerbert -NetworkNode -nextrevision -ngn -nguillaumin -Ng Zhi An -Nicholas Bollweg -Nicholas Bollweg (Nick) -Nick Kreeger -Nick Small -Nicolas Kick -Nicolò Ribaudo -Niels van Groningen -nightwing -Nikita Beloglazov -Nikita Vasilyev -Nikolay Kostov -nilp0inter -Nisarg Jhaveri -nlwillia -noragrossman -Norman Rzepka -opl- -Oreoluwa Onatemowo -Oskar Segersvärd -pablo -pabloferz -Pablo Zubieta -Page -paladox -Panupong Pasupat -paris -Paris -Paris Kasidiaris -Patil Arpith -Patrick Stoica -Patrick Strawderman -Paul Garvin -Paul Ivanov -Paul Masson -Pavel -Pavel Feldman -Pavel Petržela -Pavel Strashkin -Paweł Bartkiewicz -peteguhl -peter -Peter Flynn -peterkroon -Peter Kroon -Philipp A -Philip Stadermann -Pierre Gerold -Piët Delport -Pieter Ouwerkerk -Pontus Melke -prasanthj -Prasanth J -Prayag Verma -Radek Piórkowski -Rahul -Rahul Anand -ramwin1 -Randall Mason -Randy Burden -Randy Edmunds -Rasmus Erik Voel Jensen -Rasmus Schultz -ray ratchup -Ray Ratchup -Remi Nyborg -Renaud Durlin -Richard Denton -Richard van der Meer -Richard Z.H. Wang -Rishi Goomar -Robert Brignull -Robert Crossfield -Roberto Abdelkader Martínez Pérez -robertop23 -Robert Plummer -Rrandom -Rrrandom -Ruslan Osmanov -Ryan Petrello -Ryan Prior -sabaca -Sam Lee -Sam Rawlins -Samuel Ainsworth -Sam Wilson -sandeepshetty -Sander AKA Redsandro -Sander Verweij -santec -Sarah McAlear and Wenlin Zhang -Sascha Peilicke -satamas -satchmorun -sathyamoorthi -Saul Costa -S. Chris Colbert -SCLINIC\jdecker -Scott Aikin -Scott Goodhew -Sebastian Wilzbach -Sebastian Zaha -Sergey Goder -Sergey Tselovalnikov -Se-Won Kim -shaund -shaun gilchrist -Shawn A -Shea Bunge -sheopory -Shil S -Shiv Deepak -Shmuel Englard -Shubham Jain -Siamak Mokhtari -silverwind -Simon Edwards -sinkuu -snasa -soliton4 -sonson -spastorelli -srajanpaliwal -Stanislav Oaserele -Stas Kobzar -Stefan Borsje -Steffen Beyer -Steffen Bruchmann -Steffen Kowalski -Stephen Lavelle -Steve Champagne -Steve Hoover -Steve O'Hara -stoskov -Stu Kennedy -Sungho Kim -sverweij -Taha Jahangir -takamori -Tako Schotanus -Takuji Shimokawa -Takuya Matsuyama -Tarmil -TDaglis -tel -Tentone -tfjgeorge -Thaddee Tyl -thanasis -TheHowl -themrmax -think -Thomas Dvornik -Thomas Kluyver -Thomas Schmid -Tim Alby -Tim Baumann -Timothy Farrell -Timothy Gu -Timothy Hatcher -TobiasBg -Todd Berman -Todd Kennedy -Tomas-A -Tomas Varaneckas -Tom Erik Støwer -Tom Klancer -Tom MacWright -Tony Jian -Travis Heppe -Triangle717 -Tristan Tarrant -TSUYUSATO Kitsune -twifkak -Tyler Long -VapidWorx -Vestimir Markov -vf -Victor Bocharsky -Vincent Woo -Volker Mische -Weiyan Shao -wenli -Wes Cossick -Wesley Wiser -Weston Ruter -Will Binns-Smith -Will Dean -William Jamieson -William Stein -Willy -Wojtek Ptak -Wu Cheng-Han -Xavier Mendez -Yassin N. Hassan -YNH Webdev -Yunchi Luo -Yuvi Panda -Yvonnick Esnault -Zac Anger -Zachary Dremann -Zeno Rocha -Zhang Hao -Ziv -zziuni -魏鹏刚 diff --git a/app/src/main/assets/editor/codemirror/CHANGELOG.md b/app/src/main/assets/editor/codemirror/CHANGELOG.md deleted file mode 100644 index 4c929d24..00000000 --- a/app/src/main/assets/editor/codemirror/CHANGELOG.md +++ /dev/null @@ -1,1234 +0,0 @@ -## 5.30.0 (2017-09-20) - -### Bug fixes - -Fixed a number of issues with drawing right-to-left selections and mouse selection in bidirectional text. - -[search addon](http://codemirror.net/demo/search/): Fix crash when restarting search after doing empty search. - -[mark-selection addon](http://cm/doc/manual.html#addon_mark-selection): Fix off-by-one bug. - -[tern addon](http://codemirror.net/demo/tern.html): Fix bad request made when editing at the bottom of a large document. - -[javascript mode](http://codemirror.net/mode/javascript/): Improve parsing in a number of corner cases. - -[markdown mode](http://codemirror.net/mode/markdown/): Fix crash when a sub-mode doesn't support indentation, allow uppercase X in task lists. - -[gfm mode](http://codemirror.net/mode/gfm/): Don't highlight SHA1 'hashes' without numbers to avoid false positives. - -[soy mode](http://codemirror.net/mode/soy/): Support injected data and `@param` in comments. - -### New features - -[simple mode addon](http://codemirror.net/demo/simplemode.html): Allow groups in regexps when `token` isn't an array. - -## 5.29.0 (2017-08-24) - -### Bug fixes - -Fix crash in contentEditable input style when editing near a bookmark. - -Make sure change origins are preserved when splitting changes on [read-only marks](http://codemirror.net/doc/manual.html#mark_readOnly). - -[javascript mode](http://codemirror.net/mode/javascript/): More support for TypeScript syntax. - -[d mode](http://codemirror.net/mode/d/): Support nested comments. - -[python mode](http://codemirror.net/mode/python/): Improve tokenizing of operators. - -[markdown mode](http://codemirror.net/mode/markdown/): Further improve CommonMark conformance. - -[css mode](http://codemirror.net/mode/css/): Don't run comment tokens through the mode's state machine. - -[shell mode](http://codemirror.net/mode/shell/): Allow strings to span lines. - -[search addon](http://codemirror.net/demo/search/): Fix crash in persistent search when `extraKeys` is null. - -## 5.28.0 (2017-07-21) - -### Bug fixes - -Fix copying of, or replacing editor content with, a single dash character when copying a big selection in some corner cases. - -Make [`"goLineLeft"`](http://codemirror.net/doc/manual.html#command_goLineLeft)/`"goLineRight"` behave better on wrapped lines. - -[sql mode](http://codemirror.net/mode/sql/): Fix tokenizing of multi-dot operator and allow digits in subfield names. - -[searchcursor addon](http://codemirror.net/doc/manual.html#addon_searchcursor): Fix infinite loop on some composed character inputs. - -[markdown mode](http://codemirror.net/mode/markdown/): Make list parsing more CommonMark-compliant. - -[gfm mode](http://codemirror.net/mode/gfm/): Highlight colon syntax for emoji. - -### New features - -Expose [`startOperation`](http://codemirror.net/doc/manual.html#startOperation) and `endOperation` for explicit operation management. - -[sublime bindings](http://codemirror.net/demo/sublime.html): Add extend-selection (Ctrl-Alt- or Cmd-Shift-Up/Down). - -## 5.27.4 (2017-06-29) - -### Bug fixes - -Fix crash when using mode lookahead. - -[markdown mode](http://codemirror.net/mode/markdown/): Don't block inner mode's indentation support. - -## 5.27.2 (2017-06-22) - -### Bug fixes - -Fix crash in the [simple mode](http://codemirror.net/demo/simplemode.html)< addon. - -## 5.27.0 (2017-06-22) - -### Bug fixes - -Fix infinite loop in forced display update. - -Properly disable the hidden textarea when `readOnly` is `"nocursor"`. - -Calling the `Doc` constructor without `new` works again. - -[sql mode](http://codemirror.net/mode/sql/): Handle nested comments. - -[javascript mode](http://codemirror.net/mode/javascript/): Improve support for TypeScript syntax. - -[markdown mode](http://codemirror.net/mode/markdown/): Fix bug where markup was ignored on indented paragraph lines. - -[vim bindings](http://codemirror.net/demo/vim.html): Referencing invalid registers no longer causes an uncaught exception. - -[rust mode](http://codemirror.net/mode/rust/): Add the correct MIME type. - -[matchbrackets addon](http://codemirror.net/doc/manual.html#addon_matchbrackets): Document options. - -### New features - -Mouse button clicks can now be bound in keymaps by using names like `"LeftClick"` or `"Ctrl-Alt-MiddleTripleClick"`. When bound to a function, that function will be passed the position of the click as second argument. - -The behavior of mouse selection and dragging can now be customized with the [`configureMouse`](http://codemirror.net/doc/manual.html#option_configureMouse) option. - -Modes can now look ahead across line boundaries with the [`StringStream`](http://codemirror.net/doc/manual.html#StringStream)`.lookahead` method. - -Introduces a `"type"` token type, makes modes that recognize types output it, and add styling for it to the themes. - -New [`pasteLinesPerSelection`](http://codemirror.net/doc/manual.html#option_pasteLinesPerSelection) option to control the behavior of pasting multiple lines into multiple selections. - -[searchcursor addon](http://codemirror.net/doc/manual.html#addon_searchcursor): Support multi-line regular expression matches, and normalize strings when matching. - -## 5.26.0 (2017-05-22) - -### Bug fixes - -In textarea-mode, don't reset the input field during composition. - -More careful restoration of selections in widgets, during editor redraw. - -[javascript mode](http://codemirror.net/mode/javascript/): More TypeScript parsing fixes. - -[julia mode](http://codemirror.net/mode/julia/): Fix issue where the mode gets stuck. - -[markdown mode](http://codemirror.net/mode/markdown/): Understand cross-line links, parse all bracketed things as links. - -[soy mode](http://codemirror.net/mode/soy/): Support single-quoted strings. - -[go mode](http://codemirror.net/mode/go/): Don't try to indent inside strings or comments. - -### New features - -[vim bindings](http://codemirror.net/demo/vim.html): Parse line offsets in line or range specs. - -## 5.25.2 (2017-04-20) - -### Bug fixes - -Better handling of selections that cover the whole viewport in contentEditable-mode. - -No longer accidentally scroll the editor into view when calling `setValue`. - -Work around Chrome Android bug when converting screen coordinates to editor positions. - -Make sure long-clicking a selection sets a cursor and doesn't show the editor losing focus. - -Fix issue where pointer events were incorrectly disabled on Chrome's overlay scrollbars. - -[javascript mode](http://codemirror.net/mode/javascript/): Recognize annotations and TypeScript-style type parameters. - -[shell mode](http://codemirror.net/mode/shell/): Handle nested braces. - -[markdown mode](http://codemirror.net/mode/markdown/): Make parsing of strong/em delimiters CommonMark-compliant. - -## 5.25.0 (2017-03-20) - -### Bug fixes - -In contentEditable-mode, properly locate changes that repeat a character when inserted with IME. - -Fix handling of selections bigger than the viewport in contentEditable mode. - -Improve handling of changes that insert or delete lines in contentEditable mode. - -Count Unicode control characters 0x80 to 0x9F as special (non-printing) chars. - -Fix handling of shadow DOM roots when finding the active element. - -Add `role=presentation` to more DOM elements to improve screen reader support. - -[merge addon](http://codemirror.net/doc/manual.html#addon_merge): Make aligning of unchanged chunks more robust. - -[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Fix comment-toggling on a block of text that starts and ends in a (differnet) block comment. - -[javascript mode](http://codemirror.net/mode/javascript/): Improve support for TypeScript syntax. - -[r mode](http://codemirror.net/mode/r/): Fix indentation after semicolon-less statements. - -[shell mode](http://codemirror.net/mode/shell/): Properly handle escaped parentheses in parenthesized expressions. - -[markdown mode](http://codemirror.net/mode/markdown/): Fix a few bugs around leaving fenced code blocks. - -[soy mode](http://codemirror.net/mode/soy/): Improve indentation. - -### New features - -[lint addon](http://codemirror.net/doc/manual.html#addon_lint): Support asynchronous linters that return promises. - -[continuelist addon](http://codemirror.net/doc/manual.html#addon_continuelist): Support continuing task lists. - -[vim bindings](http://codemirror.net/demo/vim.html): Make Y behave like yy. - -[sql mode](http://codemirror.net/mode/sql/): Support sqlite dialect. - -## 5.24.2 (2017-02-22) - -### Bug fixes - -[javascript mode](http://codemirror.net/mode/javascript/): Support computed class method names. - -[merge addon](http://codemirror.net/doc/manual.html#addon_merge): Improve aligning of unchanged code in the presence of marks and line widgets. - -## 5.24.0 (2017-02-20) - -### Bug fixes - -A cursor directly before a line-wrapping break is now drawn before or after the line break depending on which direction you arrived from. - -Visual cursor motion in line-wrapped right-to-left text should be much more correct. - -Fix bug in handling of read-only marked text. - -[shell mode](http://codemirror.net/mode/shell/): Properly tokenize nested parentheses. - -[python mode](http://codemirror.net/mode/python/): Support underscores in number literals. - -[sass mode](http://codemirror.net/mode/sass/): Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset. - -[css mode](http://codemirror.net/mode/css/): Expose `lineComment` property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements. - -[julia mode](http://codemirror.net/mode/julia/): Properly indent `elseif` lines. - -[markdown mode](http://codemirror.net/mode/markdown/): Properly recognize the end of fenced code blocks when inside other markup. - -[scala mode](http://codemirror.net/mode/clike/): Improve handling of operators containing #, @, and : chars. - -[xml mode](http://codemirror.net/mode/xml/): Allow dashes in HTML tag names. - -[javascript mode](http://codemirror.net/mode/javascript/): Improve parsing of async methods, TypeScript-style comma-separated superclass lists. - -[indent-fold addon](http://codemirror.net/demo/folding.html): Ignore comment lines. - -### New features - -Positions now support a `sticky` property which determines whether they should be associated with the character before (value `"before"`) or after (value `"after"`) them. - -[vim bindings](http://codemirror.net/demo/vim.html): Make it possible to remove built-in bindings through the API. - -[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Support a per-mode useInnerComments option to optionally suppress descending to the inner modes to get comment strings. - -### Breaking changes - -The [sass mode](http://codemirror.net/mode/sass/) now depends on the [css mode](http://codemirror.net/mode/css/). - -## 5.23.0 (2017-01-19) - -### Bug fixes - -Presentation-related elements DOM elements are now marked as such to help screen readers. - -[markdown mode](http://codemirror.net/mode/markdown/): Be more picky about what HTML tags look like to avoid false positives. - -### New features - -`findModeByMIME` now understands `+json` and `+xml` MIME suffixes. - -[closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets): Add support for an `override` option to ignore language-specific defaults. - -[panel addon](http://codemirror.net/doc/manual.html#addon_panel): Add a `stable` option that auto-scrolls the content to keep it in the same place when inserting/removing a panel. - -## 5.22.2 (2017-01-12) - -### Bug fixes - -Include rollup.config.js in NPM package, so that it can be used to build from source. - -## 5.22.0 (2016-12-20) - -### Bug fixes - -[sublime bindings](http://codemirror.net/demo/sublime.html): Make `selectBetweenBrackets` work with multiple cursors. - -[javascript mode](http://codemirror.net/mode/javascript/): Fix issues with parsing complex TypeScript types, imports, and exports. - -A contentEditable editor instance with autofocus enabled no longer crashes during initializing. - -### New features - -[emacs bindings](http://codemirror.net/demo/emacs.html): Export `CodeMirror.emacs` to allow other addons to hook into Emacs-style functionality. - -[active-line addon](http://codemirror.net/doc/manual.html#addon_active-line): Add `nonEmpty` option. - -New event: [`optionChange`](http://codemirror.net/doc/manual.html#event_optionChange). - -## 5.21.0 (2016-11-21) - -### Bug fixes - -Tapping/clicking the editor in [contentEditable mode](http://codemirror.net/doc/manual.html#option_inputStyle) on Chrome now puts the cursor at the tapped position. - -Fix various crashes and misbehaviors when reading composition events in [contentEditable mode](http://codemirror.net/doc/manual.html#option_inputStyle). - -Catches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a ``. - -[merge addon](http://codemirror.net/doc/manual.html#addon_merge): Fix several issues in the chunk-aligning feature. - -[verilog mode](http://codemirror.net/mode/verilog): Rewritten to address various issues. - -[julia mode](http://codemirror.net/mode/julia): Recognize Julia 0.5 syntax. - -[swift mode](http://codemirror.net/mode/swift): Various fixes and adjustments to current syntax. - -[markdown mode](http://codemirror.net/mode/markdown): Allow lists without a blank line above them. - -### New features - -The [`setGutterMarker`](http://codemirror.net/doc/manual.html#setGutterMarker), [`clearGutter`](http://codemirror.net/doc/manual.html#clearGutter), and [`lineInfo`](http://codemirror.net/doc/manual.html#lineInfo) methods are now available on `Doc` objects. - -The [`heightAtLine`](http://codemirror.net/doc/manual.html#heightAtLine) method now takes an extra argument to allow finding the height at the top of the line's line widgets. - -[ruby mode](http://codemirror.net/mode/ruby): `else` and `elsif` are now immediately indented. - -[vim bindings](http://codemirror.net/demo/vim.html): Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode. - -## 5.20.2 (2016-10-21) - -### Bug fixes - -Fix `CodeMirror.version` returning the wrong version number. - -## 5.20.0 (2016-10-20) - -### Bug fixes - -Make `newlineAndIndent` command work with multiple cursors on the same line. - -Make sure keypress events for backspace are ignored. - -Tokens styled with overlays no longer get a nonsense `cm-cm-overlay` class. - -Line endings for pasted content are now normalized to the editor's [preferred ending](http://codemirror.net/doc/manual.html#option_lineSeparator). - -[javascript mode](http://codemirror.net/mode/javascript): Improve support for class expressions. Support TypeScript optional class properties, the `abstract` keyword, and return type declarations for arrow functions. - -[css mode](http://codemirror.net/mode/css): Fix highlighting of mixed-case keywords. - -[closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets): Improve behavior when typing a quote before a string. - -### New features - -The core is now maintained as a number of small files, using ES6 syntax and modules, under the `src/` directory. A git checkout no longer contains a working `codemirror.js` until you `npm build` (but when installing from NPM, it is included). - -The [`refresh`](http://codemirror.net/doc/manual.html#event_refresh) event is now documented and stable. - -## 5.19.0 (2016-09-20) - -### Bugfixes - -[erlang mode](http://codemirror.net/mode/erlang): Fix mode crash when trying to read an empty context. - -[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Fix broken behavior when toggling comments inside a comment. - -xml-fold addon: Fix a null-dereference bug. - -Page up and page down now do something even in single-line documents. - -Fix an issue where the cursor position could be off in really long (~8000 character) tokens. - -### New features - -[javascript mode](http://codemirror.net/mode/javascript): Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the `type` keyword. - -The [`blur`](http://codemirror.net/doc/manual.html#event_blur) and [`focus`](http://codemirror.net/doc/manual.html#event_focus) events now pass the DOM event to their handlers. - -## 5.18.2 (2016-08-23) - -### Bugfixes - -[vue mode](http://codemirror.net/mode/vue): Fix outdated references to renamed Pug mode dependency. - -## 5.18.0 (2016-08-22) - -### Bugfixes - -Make sure [gutter backgrounds](http://codemirror.net/doc/manual.html#addLineClass) stick to the rest of the gutter during horizontal scrolling. - -The contenteditable [`inputStyle`](http://codemirror.net/doc/manual.html#option_inputStyle) now properly supports pasting on pre-Edge IE versions. - -[javascript mode](http://codemirror.net/mode/javascript): Fix some small parsing bugs and improve TypeScript support. - -[matchbrackets addon](http://codemirror.net/doc/manual.html#addon_matchbrackets): Fix bug where active highlighting was left in editor when the addon was disabled. - -[match-highlighter addon](http://codemirror.net/doc/manual.html#addon_match-highlighter): Only start highlighting things when the editor gains focus. - -[javascript-hint addon](http://codemirror.net/doc/manual.html#addon_javascript-hint): Also complete non-enumerable properties. - -### New features - -The [`addOverlay`](http://codemirror.net/doc/manual.html#addOverlay) method now supports a `priority` option to control the order in which overlays are applied. - -MIME types that end in `+json` now default to the JSON mode when the MIME itself is not defined. - -### Breaking changes - -The mode formerly known as Jade was renamed to [Pug](http://codemirror.net/mode/pug). - -The [Python mode](http://codemirror.net/mode/python) now defaults to Python 3 (rather than 2) syntax. - -## 5.17.0 (2016-07-19) - -### Bugfixes - -Fix problem with wrapped trailing whitespace displaying incorrectly. - -Prevent IME dialog from overlapping typed content in Chrome. - -Improve measuring of characters near a line wrap. - -[javascript mode](http://codemirror.net/mode/javascript): Improve support for `async`, allow trailing commas in `import` lists. - -[vim bindings](http://codemirror.net/demo/vim.html): Fix backspace in replace mode. - -[sublime bindings](http://codemirror.net/demo/sublime.html): Fix some key bindings on OS X to match Sublime Text. - -### New features - -[markdown mode](http://codemirror.net/mode/markdown): Add more classes to image links in highlight-formatting mode. - -## 5.16.0 (2016-06-20) - -### Bugfixes - -Fix glitches when dragging content caused by the drop indicator receiving mouse events. - -Make Control-drag work on Firefox. - -Make clicking or selection-dragging at the end of a wrapped line select the right position. - -[show-hint addon](http://codemirror.net/doc/manual.html#addon_show-hint): Prevent widget scrollbar from hiding part of the hint text. - -[rulers addon](http://codemirror.net/doc/manual.html#addon_rulers): Prevent rulers from forcing a horizontal editor scrollbar. - -### New features - -[search addon](http://codemirror.net/doc/manual.html#addon_search): Automatically bind search-related keys in persistent dialog. - -[sublime keymap](http://codemirror.net/demo/sublime.html): Add a multi-cursor aware smart backspace binding. - -## 5.15.2 (2016-05-20) - -### Bugfixes - -Fix a critical document corruption bug that occurs when a document is gradually grown. - -## 5.15.0 (2016-05-20) - -### Bugfixes - -Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode. - -Fix issue where not all ASCII control characters were being replaced by placeholders. - -Remove the assumption that all modes have a `startState` method from several wrapping modes. - -Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any. - -Optimize document tree building when loading or pasting huge chunks of content. - -[markdown mode](http://codemirror.net/mode/markdown/): Fix several issues in matching link targets. - -[clike mode](http://codemirror.net/mode/clike/): Improve indentation of C++ template declarations. - -### New features - -Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected. - -Pasting [linewise-copied](http://codemirror.net/doc/manual.html#option_lineWiseCopyCut) content when there is no selection now inserts the lines above the current line. - -[javascript mode](http://codemirror.net/mode/javascript/): Support `async`/`await` and improve support for TypeScript type syntax. - -## 5.14.2 (2016-04-20) - -### Bugfixes - -Push a new package to NPM due to an [NPM bug](https://github.com/npm/npm/issues/5082) omitting the LICENSE file in 5.14.0. - -Set `dataTransfer.effectAllowed` in `dragstart` handler to help browsers use the right drag icon. - -Add the [mbox mode](http://codemirror.net/mode/mbox/index.html) to `mode/meta.js`. - -## 5.14.0 (2016-04-20) - -### Bugfixes - -[`posFromIndex`](http://codemirror.net/doc/manual.html#posFromIndex) and [`indexFromPos`](http://codemirror.net/doc/manual.html#indexFromPos) now take [`lineSeparator`](http://codemirror.net/doc/manual.html#option_lineSeparator) into account. - -[vim bindings](http://codemirror.net/demo/vim.html): Only call `.save()` when it is actually available. - -[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Be careful not to mangle multi-line strings. - -[Python mode](http://codemirror.net/mode/python/index.html): Improve distinguishing of decorators from `@` operators. - -[`findMarks`](http://codemirror.net/doc/manual.html#findMarks): No longer return marks that touch but don't overlap given range. - -### New features - -[vim bindings](http://codemirror.net/demo/vim.html): Add yank command. - -[match-highlighter addon](http://codemirror.net/doc/manual.html#addon_match-highlighter): Add `trim` option to disable ignoring of whitespace. - -[PowerShell mode](http://codemirror.net/mode/powershell/index.html): Added. - -[Yacas mode](http://codemirror.net/mode/yacas/index.html): Added. - -[Web IDL mode](http://codemirror.net/mode/webidl/index.html): Added. - -[SAS mode](http://codemirror.net/mode/sas/index.html): Added. - -[mbox mode](http://codemirror.net/mode/mbox/index.html): Added. - -## 5.13.2 (2016-03-23) - -### Bugfixes - -Solves a problem where the gutter would sometimes not extend all the way to the end of the document. - -## 5.13.0 (2016-03-21) - -### New features - -New DOM event forwarded: [`"dragleave"`](http://codemirror.net/doc/manual.html#event_dom). - -[protobuf mode](http://codemirror.net/mode/protobuf/index.html): Newly added. - -### Bugfixes - -Fix problem where [`findMarks`](http://codemirror.net/doc/manual.html#findMarks) sometimes failed to find multi-line marks. - -Fix crash that showed up when atomic ranges and bidi text were combined. - -[show-hint addon](http://codemirror.net/demo/complete.html): Completion widgets no longer close when the line indented or dedented. - -[merge addon](http://codemirror.net/demo/merge.html): Fix bug when merging chunks at the end of the file. - -[placeholder addon](http://codemirror.net/doc/manual.html#addon_placeholder): No longer gets confused by [`swapDoc`](http://codemirror.net/doc/manual.html#swapDoc). - -[simplescrollbars addon](http://codemirror.net/doc/manual.html#addon_simplescrollbars): Fix invalid state when deleting at end of document. - -[clike mode](http://codemirror.net/mode/clike/index.html): No longer gets confused when a comment starts after an operator. - -[markdown mode](http://codemirror.net/mode/markdown/index.html): Now supports CommonMark-style flexible list indentation. - -[dylan mode](http://codemirror.net/mode/dylan/index.html): Several improvements and fixes. - -## 5.12.0 (2016-02-19) - -### New features - -[Vim bindings](http://codemirror.net/demo/vim.html): Ctrl-Q is now an alias for Ctrl-V. - -[Vim bindings](http://codemirror.net/demo/vim.html): The Vim API now exposes an `unmap` method to unmap bindings. - -[active-line addon](http://codemirror.net/demo/activeline.html): This addon can now style the active line's gutter. - -[FCL mode](http://codemirror.net/mode/fcl/): Newly added. - -[SQL mode](http://codemirror.net/mode/sql/): Now has a Postgresql dialect. - -### Bugfixes - -Fix [issue](https://github.com/codemirror/CodeMirror/issues/3781) where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly. - -Use absolute, rather than fixed positioning in the context-menu intercept hack, to work around a [problem](https://github.com/codemirror/CodeMirror/issues/3238) when the editor is inside a transformed parent container. - -Solve a [problem](https://github.com/codemirror/CodeMirror/issues/3821) where the horizontal scrollbar could hide text in Firefox. - -Fix a [bug](https://github.com/codemirror/CodeMirror/issues/3834) that caused phantom scroll space under the text in some situations. - -[Sublime Text bindings](http://codemirror.net/demo/sublime.html): Bind delete-line to Shift-Ctrl-K on OS X. - -[Markdown mode](http://codemirror.net/mode/markdown/): Fix [issue](https://github.com/codemirror/CodeMirror/issues/3787) where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses. - -[Markdown mode](http://codemirror.net/mode/markdown/): Ignore backslashes in code fragments. - -[Markdown mode](http://codemirror.net/mode/markdown/): Use whichever mode is registered as `text/html` to parse HTML. - -[Clike mode](http://codemirror.net/mode/clike/): Improve indentation of Scala `=>` functions. - -[Python mode](http://codemirror.net/mode/python/): Improve indentation of bracketed code. - -[HTMLMixed mode](http://codemirror.net/mode/htmlmixed/): Support multi-line opening tags for sub-languages (` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CodeMirrorEditor - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/assets/editor/index.js b/app/src/main/assets/editor/index.js deleted file mode 100644 index 0bd5833d..00000000 --- a/app/src/main/assets/editor/index.js +++ /dev/null @@ -1,27 +0,0 @@ - -var editor = CodeMirror(document.body, { - lineNumbers: true, // 显示行数 - indentUnit: 4, // 缩进单位为4 - styleActiveLine: true, // 当前行背景高亮 - foldGutter: true, - gutters:["CodeMirror-linenumbers", "CodeMirror-foldgutter"], - matchBrackets: true, // 括号匹配 - mode: 'javascript', - lineWrapping: false, // 自动换行 - theme: 'neo' // 使用neo主题 -}); -editor.setOption("fullScreen", true); - -editor.setCursor({line: 0, ch: 0}); -editor.on('change', function(){ - var c = editor.getCursor(); - if(typeof(__bridge__) != 'undefined'){ - __bridge__.onKeyUp(editor.getLine(c.line), c.ch); - }else{ - console.log(editor.getLine(c.line), c.ch); - } -}); - -window.onload = function(){ - autojs(editor); -} \ No newline at end of file diff --git a/app/src/main/assets/editor/theme/dark_default.json b/app/src/main/assets/editor/theme/dark_default.json new file mode 100644 index 00000000..4823a374 --- /dev/null +++ b/app/src/main/assets/editor/theme/dark_default.json @@ -0,0 +1,14 @@ +{ + "$schema": "vscode://schemas/color-theme", + "name": "Dark Default Colors", + "colors": { + "editor.background": "#1E1E1E", + "editor.foreground": "#D4D4D4", + "editor.inactiveSelectionBackground": "#3A3D41", + "editorIndentGuide.background": "#404040", + "editor.selectionHighlightBackground": "#ADD6FF26", + "list.dropBackground": "#383B3D", + "activityBarBadge.background": "#007ACC", + "sideBarTitle.foreground": "#BBBBBB" + } +} \ No newline at end of file diff --git a/app/src/main/assets/editor/theme/dark_plus.json b/app/src/main/assets/editor/theme/dark_plus.json new file mode 100644 index 00000000..9eb65e27 --- /dev/null +++ b/app/src/main/assets/editor/theme/dark_plus.json @@ -0,0 +1,514 @@ +{ + "$schema": "vscode://schemas/color-theme", + "name": "Dark (Visual Studio)", + "colors": { + "editor.background": "#1E1E1E", + "editor.foreground": "#D4D4D4", + "editor.inactiveSelectionBackground": "#3A3D41", + "editorIndentGuide.background": "#404040", + "editor.selectionHighlightBackground": "#ADD6FF26", + "imeBar.background": "#dd1e1e1e", + "imeBar.foreground": "#f1f1f1" + }, + "tokenColors": [ + { + "scope": [ + "meta.embedded", + "source.groovy.embedded" + ], + "settings": { + "foreground": "#D4D4D4" + } + }, + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "header", + "settings": { + "foreground": "#000080" + } + }, + { + "scope": "comment", + "settings": { + "foreground": "#608b4e" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": [ + "constant.numeric" + ], + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "constant.regexp", + "settings": { + "foreground": "#646695" + } + }, + { + "scope": "entity.name.tag", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "entity.name.tag.css", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": [ + "entity.other.attribute-name.class.css", + "entity.other.attribute-name.class.mixin.css", + "entity.other.attribute-name.id.css", + "entity.other.attribute-name.parent-selector.css", + "entity.other.attribute-name.pseudo-class.css", + "entity.other.attribute-name.pseudo-element.css", + "source.css.less entity.other.attribute-name.id", + "entity.other.attribute-name.attribute.scss", + "entity.other.attribute-name.scss" + ], + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "invalid", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "scope": "markup.bold", + "settings": { + "fontStyle": "bold", + "foreground": "#569cd6" + } + }, + { + "scope": "markup.heading", + "settings": { + "fontStyle": "bold", + "foreground": "#569cd6" + } + }, + { + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.inserted", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "markup.deleted", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "markup.changed", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "beginning.punctuation.definition.quote.markdown", + "settings": { + "foreground": "#608b4e" + } + }, + { + "scope": "beginning.punctuation.definition.list.markdown", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "markup.inline.raw", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "meta.selector", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "name": "brackets of XML/HTML tags", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#808080" + } + }, + { + "scope": "meta.preprocessor", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "meta.preprocessor.string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "meta.preprocessor.numeric", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "meta.structure.dictionary.key.python", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "meta.diff.header", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.modifier", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.tag", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.value", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.regexp", + "settings": { + "foreground": "#d16969" + } + }, + { + "name": "String interpolation", + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "name": "Reset JavaScript string interpolation expression", + "scope": [ + "meta.template.expression" + ], + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "variable.css", + "variable.scss", + "variable.other.less", + "source.coffee.embedded" + ], + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "keyword", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": [ + "keyword.operator.new", + "keyword.operator.expression", + "keyword.operator.cast", + "keyword.operator.sizeof", + "keyword.operator.logical.python" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.other.unit", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "support.function.git-rebase", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "constant.sha.git-rebase", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "name": "coloring of the Java import and package identifiers", + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#569cd6" + } + }, + { + "name": "Function declarations", + "scope": [ + "entity.name.function", + "support.function", + "support.constant.handlebars" + ], + "settings": { + "foreground": "#DCDCAA" + } + }, + { + "name": "Types declaration and references", + "scope": [ + "meta.return-type", + "support.class", + "support.type", + "entity.name.type", + "entity.name.class", + "storage.type.cs", + "storage.type.generic.cs", + "storage.type.modifier.cs", + "storage.type.variable.cs", + "storage.type.annotation.java", + "storage.type.generic.java", + "storage.type.java", + "storage.type.object.array.java", + "storage.type.primitive.array.java", + "storage.type.primitive.java", + "storage.type.token.java", + "storage.type.groovy", + "storage.type.annotation.groovy", + "storage.type.parameters.groovy", + "storage.type.generic.groovy", + "storage.type.object.array.groovy", + "storage.type.primitive.array.groovy", + "storage.type.primitive.groovy" + ], + "settings": { + "foreground": "#4EC9B0" + } + }, + { + "name": "Types declaration and references, TS grammar specific", + "scope": [ + "meta.type.cast.expr", + "meta.type.new.expr", + "support.constant.math", + "support.constant.dom", + "support.constant.json", + "entity.other.inherited-class" + ], + "settings": { + "foreground": "#4EC9B0" + } + }, + { + "name": "Control flow keywords", + "scope": "keyword.control", + "settings": { + "foreground": "#C586C0" + } + }, + { + "name": "Variable and parameter name", + "scope": [ + "variable", + "meta.definition.variable.name", + "support.variable" + ], + "settings": { + "foreground": "#9CDCFE" + } + }, + { + "name": "Object keys, TS grammar specific", + "scope": [ + "meta.object-literal.key" + ], + "settings": { + "foreground": "#9CDCFE" + } + }, + { + "name": "CSS property value", + "scope": [ + "support.constant.property-value", + "support.constant.font-name", + "support.constant.media-type", + "support.constant.media", + "constant.other.color.rgb-value", + "constant.other.rgb-value", + "support.constant.color" + ], + "settings": { + "foreground": "#CE9178" + } + }, + { + "name": "Regular expression groups", + "scope": [ + "punctuation.definition.group.regexp", + "punctuation.definition.group.assertion.regexp", + "punctuation.definition.character-class.regexp", + "punctuation.character.set.begin.regexp", + "punctuation.character.set.end.regexp", + "keyword.operator.negation.regexp", + "support.other.parenthesis.regexp" + ], + "settings": { + "foreground": "#CE9178" + } + }, + { + "scope": [ + "constant.character.character-class.regexp", + "constant.other.character-class.set.regexp", + "constant.other.character-class.regexp", + "constant.character.set.regexp" + ], + "settings": { + "foreground": "#d16969" + } + }, + { + "scope": [ + "keyword.operator.or.regexp", + "keyword.control.anchor.regexp" + ], + "settings": { + "foreground": "#DCDCAA" + } + }, + { + "scope": "keyword.operator.quantifier.regexp", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "constant.character", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "constant.character.escape", + "settings": { + "foreground": "#d7ba7d" + } + } + ] +} \ No newline at end of file diff --git a/app/src/main/assets/editor/theme/dark_vs.json b/app/src/main/assets/editor/theme/dark_vs.json new file mode 100644 index 00000000..cfd99150 --- /dev/null +++ b/app/src/main/assets/editor/theme/dark_vs.json @@ -0,0 +1,364 @@ +{ + "$schema": "vscode://schemas/color-theme", + "name": "Dark (Visual Studio)", + "colors": { + "editor.background": "#1E1E1E", + "editor.foreground": "#D4D4D4", + "editor.inactiveSelectionBackground": "#3A3D41", + "editorIndentGuide.background": "#404040", + "editor.selectionHighlightBackground": "#ADD6FF26", + "list.dropBackground": "#383B3D", + "activityBarBadge.background": "#007ACC", + "sideBarTitle.foreground": "#BBBBBB" + }, + "tokenColors": [ + { + "scope": [ + "meta.embedded", + "source.groovy.embedded" + ], + "settings": { + "foreground": "#D4D4D4" + } + }, + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "header", + "settings": { + "foreground": "#000080" + } + }, + { + "scope": "comment", + "settings": { + "foreground": "#608b4e" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": [ + "constant.numeric" + ], + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "constant.regexp", + "settings": { + "foreground": "#646695" + } + }, + { + "scope": "entity.name.tag", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "entity.name.tag.css", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": [ + "entity.other.attribute-name.class.css", + "entity.other.attribute-name.class.mixin.css", + "entity.other.attribute-name.id.css", + "entity.other.attribute-name.parent-selector.css", + "entity.other.attribute-name.pseudo-class.css", + "entity.other.attribute-name.pseudo-element.css", + "source.css.less entity.other.attribute-name.id", + "entity.other.attribute-name.attribute.scss", + "entity.other.attribute-name.scss" + ], + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "invalid", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "scope": "markup.bold", + "settings": { + "fontStyle": "bold", + "foreground": "#569cd6" + } + }, + { + "scope": "markup.heading", + "settings": { + "fontStyle": "bold", + "foreground": "#569cd6" + } + }, + { + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.inserted", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "markup.deleted", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "markup.changed", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "beginning.punctuation.definition.quote.markdown", + "settings": { + "foreground": "#608b4e" + } + }, + { + "scope": "beginning.punctuation.definition.list.markdown", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "markup.inline.raw", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "meta.selector", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "name": "brackets of XML/HTML tags", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#808080" + } + }, + { + "scope": "meta.preprocessor", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "meta.preprocessor.string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "meta.preprocessor.numeric", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "meta.structure.dictionary.key.python", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "meta.diff.header", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.modifier", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.tag", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.value", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.regexp", + "settings": { + "foreground": "#d16969" + } + }, + { + "name": "String interpolation", + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "name": "Reset JavaScript string interpolation expression", + "scope": [ + "meta.template.expression" + ], + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "variable.css", + "variable.scss", + "variable.other.less", + "source.coffee.embedded" + ], + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "keyword", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": [ + "keyword.operator.new", + "keyword.operator.expression", + "keyword.operator.cast", + "keyword.operator.sizeof", + "keyword.operator.logical.python" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.other.unit", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "support.function.git-rebase", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "constant.sha.git-rebase", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "name": "coloring of the Java import and package identifiers", + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#569cd6" + } + } + ] +} \ No newline at end of file diff --git a/app/src/main/java/com/stardust/scriptdroid/model/editor/EditorColors.java b/app/src/main/java/com/stardust/scriptdroid/model/editor/EditorColors.java new file mode 100644 index 00000000..d7ddee7e --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/model/editor/EditorColors.java @@ -0,0 +1,79 @@ + +package com.stardust.scriptdroid.model.editor; + + +import com.google.gson.annotations.SerializedName; + +public class EditorColors { + + @SerializedName("editor.background") + private String mEditorBackground; + @SerializedName("editor.foreground") + private String mEditorForeground; + @SerializedName("editor.inactiveSelectionBackground") + private String mEditorInactiveSelectionBackground; + @SerializedName("editorIndentGuide.background") + private String mEditorIndentGuideBackground; + @SerializedName("editor.selectionHighlightBackground") + private String mEditorSelectionHighlightBackground; + @SerializedName("imeBar.background") + private String mImeBackgroundColor; + @SerializedName("imeBar.foreground") + private String mImeForegroundColor; + + public String getEditorBackground() { + return mEditorBackground; + } + + public void setEditorBackground(String editorBackground) { + mEditorBackground = editorBackground; + } + + public String getEditorForeground() { + return mEditorForeground; + } + + public void setEditorForeground(String editorForeground) { + mEditorForeground = editorForeground; + } + + public String getEditorInactiveSelectionBackground() { + return mEditorInactiveSelectionBackground; + } + + public void setEditorInactiveSelectionBackground(String editorInactiveSelectionBackground) { + mEditorInactiveSelectionBackground = editorInactiveSelectionBackground; + } + + public String getEditorIndentGuideBackground() { + return mEditorIndentGuideBackground; + } + + public void setEditorIndentGuideBackground(String editorIndentGuideBackground) { + mEditorIndentGuideBackground = editorIndentGuideBackground; + } + + public String getEditorSelectionHighlightBackground() { + return mEditorSelectionHighlightBackground; + } + + public void setEditorSelectionHighlightBackground(String editorSelectionHighlightBackground) { + mEditorSelectionHighlightBackground = editorSelectionHighlightBackground; + } + + public String getImeBackgroundColor() { + return mImeBackgroundColor; + } + + public void setImeBackgroundColor(String imeBackgroundColor) { + mImeBackgroundColor = imeBackgroundColor; + } + + public String getImeForegroundColor() { + return mImeForegroundColor; + } + + public void setImeForegroundColor(String imeForegroundColor) { + mImeForegroundColor = imeForegroundColor; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/model/editor/EditorTheme.java b/app/src/main/java/com/stardust/scriptdroid/model/editor/EditorTheme.java new file mode 100644 index 00000000..fbfd94bb --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/model/editor/EditorTheme.java @@ -0,0 +1,78 @@ + +package com.stardust.scriptdroid.model.editor; + +import android.graphics.Color; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; + +import java.io.Reader; +import java.util.Collections; +import java.util.List; + +import static org.mozilla.javascript.Token.*; + +public class EditorTheme { + + private static Gson sGson; + + static { + sGson = new GsonBuilder() + .registerTypeAdapter(TokenColor.class, new TokenColorDeserializer()) + .create(); + } + + @SerializedName("colors") + private EditorColors mEditorColors; + + @SerializedName("name") + private String mName; + + @SerializedName("$schema") + private String mSchema; + + @SerializedName("tokenColors") + private List mTokenColors = Collections.emptyList(); + + public static EditorTheme fromJson(String json) { + return sGson.fromJson(json, EditorTheme.class); + } + + public static EditorTheme fromJson(Reader json) { + return sGson.fromJson(json, EditorTheme.class); + } + + public EditorColors getEditorColors() { + return mEditorColors; + } + + public void setEditorColors(EditorColors editorColors) { + mEditorColors = editorColors; + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public String getSchema() { + return mSchema; + } + + public void setSchema(String schema) { + mSchema = schema; + } + + public List getTokenColors() { + return mTokenColors; + } + + public void setTokenColors(List tokenColors) { + mTokenColors = tokenColors; + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColor.java b/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColor.java new file mode 100644 index 00000000..d50768c7 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColor.java @@ -0,0 +1,49 @@ +package com.stardust.scriptdroid.model.editor; + +import com.google.gson.annotations.SerializedName; + +import java.util.Collections; +import java.util.List; + +/** + * Created by Stardust on 2018/2/21. + */ + +public class TokenColor { + + @SerializedName("name") + private String mName; + + @SerializedName("scope") + private List mScope = Collections.emptyList(); + + @SerializedName("settings") + private TokenColorSettings mSettings; + + public TokenColor() { + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public List getScope() { + return mScope; + } + + public void setScope(List scope) { + mScope = scope; + } + + public TokenColorSettings getSettings() { + return mSettings; + } + + public void setSettings(TokenColorSettings settings) { + mSettings = settings; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColorDeserializer.java b/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColorDeserializer.java new file mode 100644 index 00000000..501bc2c0 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColorDeserializer.java @@ -0,0 +1,51 @@ +package com.stardust.scriptdroid.model.editor; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.mozilla.javascript.Token; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by Stardust on 2018/2/21. + */ + +public class TokenColorDeserializer implements JsonDeserializer { + + @Override + public TokenColor deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject object = json.getAsJsonObject(); + TokenColor tokenColor = new TokenColor(); + if (object.has("name")) { + tokenColor.setName(object.get("name").getAsString()); + } + if (object.has("settings")) { + tokenColor.setSettings(context.deserialize(object.get("settings"), TokenColorSettings.class)); + } + if (object.has("scope")) { + tokenColor.setScope(deserializeAsList(object, "scope")); + } + return tokenColor; + } + + private List deserializeAsList(JsonObject object, String key) { + JsonElement scope = object.get(key); + if (scope.isJsonArray()) { + ArrayList list = new ArrayList<>(); + for (JsonElement e : scope.getAsJsonArray()) { + list.add(e.getAsString()); + } + return list; + } else { + return Collections.singletonList(scope.getAsString()); + } + } +} + diff --git a/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColorSettings.java b/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColorSettings.java new file mode 100644 index 00000000..dce633a2 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/model/editor/TokenColorSettings.java @@ -0,0 +1,35 @@ +package com.stardust.scriptdroid.model.editor; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Stardust on 2018/2/21. + */ + +public class TokenColorSettings { + + @SerializedName("foreground") + private String mForeground; + + @SerializedName("fontStyle") + private String mFontStyle; + + public TokenColorSettings() { + } + + public String getForeground() { + return mForeground; + } + + public void setForeground(String foreground) { + mForeground = foreground; + } + + public String getFontStyle() { + return mFontStyle; + } + + public void setFontStyle(String fontStyle) { + mFontStyle = fontStyle; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/CodeMirrorEditor.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/CodeMirrorEditor.java deleted file mode 100644 index ceee55a5..00000000 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/CodeMirrorEditor.java +++ /dev/null @@ -1,499 +0,0 @@ -package com.stardust.scriptdroid.ui.edit; - -import android.content.Context; -import android.graphics.Color; -import android.os.Build; -import android.support.annotation.RequiresApi; -import android.support.design.widget.Snackbar; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.ActionMode; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.webkit.ConsoleMessage; -import android.webkit.JavascriptInterface; -import android.webkit.JsPromptResult; -import android.webkit.JsResult; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.FrameLayout; -import android.widget.Toast; - -import com.stardust.pio.PFiles; -import com.stardust.scriptdroid.R; -import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; -import com.stardust.util.ClipboardUtil; - -import org.jdeferred.Deferred; -import org.jdeferred.impl.DeferredObject; - -import java.io.File; -import java.io.IOException; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import io.reactivex.subjects.PublishSubject; -import me.zhanghai.android.materialprogressbar.MaterialProgressBar; - -/** - * Created by Stardust on 2017/9/27. - */ - -public class CodeMirrorEditor extends FrameLayout { - - private static final String LOG_TAG = "CodeMirrorEditor"; - - - public interface Callback { - - void onKeyUp(String line, int ch); - - } - - private static String[] sAvailableThemes; - private String mTheme = "neo"; - private FrameLayout mProgressBarContainer; - private WebView mWebView; - private JavaScriptBridge mJavaScriptBridge = new JavaScriptBridge(); - private Callback mCallback; - private Deferred mPageFinished = new DeferredObject<>(); - - public CodeMirrorEditor(Context context) { - super(context); - init(); - } - - public CodeMirrorEditor(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public CodeMirrorEditor(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public CodeMirrorEditor(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - - public WebView getWebView() { - return mWebView; - } - - public void setCallback(Callback callback) { - mCallback = callback; - } - - public void beautifyCode() { - evalJavaScript("editor.setValue(js_beautify(editor.getValue(), {indent_size: 2}));"); - } - - public String[] getAvailableThemes() { - if (sAvailableThemes == null) { - try { - sAvailableThemes = getResources().getAssets().list("editor/codemirror/theme"); - for (int i = 0; i < sAvailableThemes.length; i++) { - sAvailableThemes[i] = PFiles.getNameWithoutExtension(sAvailableThemes[i]); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return sAvailableThemes; - } - - - public String getTheme() { - return mTheme; - } - - public void setTheme(final String theme) { - mPageFinished.promise().done(result -> { - evalJavaScript(String.format(Locale.getDefault(), - "var e = document.createElement('link');e.setAttribute('rel', 'stylesheet');" + - "e.setAttribute('type', 'text/css');e.setAttribute('href', '%s');" + - "document.getElementsByTagName('head')[0].appendChild(e);", - "codemirror/theme/" + theme + ".css")); - evalJavaScript(String.format(Locale.getDefault(), "editor.setOption('theme', '%s');", theme)); - mTheme = theme; - }); - } - - - private void init() { - setupWebView(); - setupProgress(); - mWebView.loadUrl("file:///android_asset/editor/index.html"); - } - - private void setupProgress() { - mProgressBarContainer = new FrameLayout(getContext()); - mProgressBarContainer.setBackgroundColor(Color.WHITE); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - addView(mProgressBarContainer, params); - MaterialProgressBar progressBar = new MaterialProgressBar(getContext()); - int dp50 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()); - FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(dp50, dp50); - p.gravity = Gravity.CENTER; - mProgressBarContainer.addView(progressBar, p); - } - - private void setupWebView() { - mWebView = new WebView(getContext()) { - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - InputConnection connection = super.onCreateInputConnection(outAttrs); - return connection;// == null ? null : new MyInputConnection(connection); - } - }; - setupWebSettings(); - mWebView.addJavascriptInterface(mJavaScriptBridge, "__bridge__"); - addView(mWebView); - } - - - private void setupWebSettings() { - WebSettings settings = mWebView.getSettings(); - settings.setBuiltInZoomControls(true); - //settings.setLoadWithOverviewMode(true); - settings.setJavaScriptEnabled(true); - settings.setJavaScriptCanOpenWindowsAutomatically(true); - settings.setDomStorageEnabled(true); - settings.setNeedInitialFocus(true); - settings.setDisplayZoomControls(false); - mWebView.setWebChromeClient(new MyWebChromeClient()); - mWebView.setWebViewClient(new MyWebViewClient()); - - } - - - @Override - public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { - return super.startActionModeForChild(originalView, new EditorActionModeCallback(callback, this)); - } - - @Override - public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback, int type) { - return super.startActionModeForChild(originalView, new EditorActionModeCallback(callback, this), type); - } - - public void setReadOnly(boolean readOnly) { - evalJavaScript(String.format("editor.setOption('readOnly', %b);", readOnly)); - } - - public void setProgress(boolean onProgress) { - mProgressBarContainer.setVisibility(onProgress ? VISIBLE : GONE); - } - - public void setText(final String text) { - int id = SharedVariables.put(text); - mPageFinished.promise().done(result -> evalJavaScript(String.format(Locale.getDefault(), - "editor.setValue(__bridge__.getStringFromAndroid(%d));", id))); - } - - - public void insert(String text) { - int id = SharedVariables.put(text); - mPageFinished.promise().done(result -> evalJavaScript(String.format(Locale.getDefault(), - "editor.replaceSelection(__bridge__.getStringFromAndroid(%d));", id))); - } - - - public Observable getText() { - return evalString("editor.getValue()"); - } - - public Observable getLine() { - return evalString("editor.getLine(editor.getCursor().line)"); - } - - public void deleteLine() { - evalJavaScript("editor.replaceRange('', {line: editor.getCursor().line, ch: 0}," + - " {line: editor.getCursor().line + 1, ch: 0});"); - } - - public void moveCursor(int dLine, int dCh) { - evalJavaScript(String.format(Locale.getDefault(), - "editor.setCursor({line: editor.getCursor().line + (%d), ch: editor.getCursor().ch + (%d)});", - dLine, dCh)); - } - - public void copyLine() { - getLine() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> { - ClipboardUtil.setClip(getContext(), s); - Snackbar.make(mWebView, R.string.text_already_copy_to_clip, Snackbar.LENGTH_SHORT).show(); - }); - } - - public void cut() { - getSelection().observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> { - ClipboardUtil.setClip(getContext(), s); - evalJavaScript("editor.replaceSelection('');"); - }); - } - - - public void copy() { - getSelection().observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> ClipboardUtil.setClip(getContext(), s)); - } - - public void paste() { - CharSequence clip = ClipboardUtil.getClip(getContext()); - if (clip == null) { - return; - } - evalJavaScript(String.format("editor.replaceSelection('%s');", clip.toString().replace("'", "\\'"))); - } - - - public void selectAll() { - evalJavaScript("editor.execCommand('selectAll');"); - } - - public Observable getLineCount() { - PublishSubject publishSubject = PublishSubject.create(); - int id = SharedVariables.put(publishSubject); - evalJavaScript(String.format(Locale.getDefault(), "__bridge__.setIntFromJs(%d, editor.lineCount())", id)); - return publishSubject; - } - - - public void undo() { - evalJavaScript("editor.undo();"); - } - - public void redo() { - evalJavaScript("editor.redo();"); - } - - public void jumpTo(int line, int col) { - evalJavaScript("editor.setCursor({line: " + line + ", ch: " + col + "});"); - } - - public void find(String keywords, boolean usingRegex) { - setQuery(keywords, usingRegex); - findNext(); - } - - private void setQuery(String keywords, boolean usingRegex) { - keywords = keywords.replace("'", "\'"); - if (usingRegex) { - keywords = "/" + keywords + "/"; - } - evalJavaScript("editor.execCommand('clearSearch'); editor.state.search.query = '" + - keywords + "';"); - } - - public void findNext() { - evalJavaScript("editor.execCommand('findNext');"); - } - - public void findPrev() { - evalJavaScript("editor.execCommand('findPrev');"); - } - - public void replaceAll(String keywords, String replacement, boolean usingRegex) { - setQuery(keywords, usingRegex); - replacement = replacement.replace("'", "\'"); - evalJavaScript("editor.state.search.text = '" + replacement + "';"); - evalJavaScript("editor.execCommand('replaceAll');"); - } - - public void jumpToDef() { - evalJavaScript("editor.tern.jumpToDef();"); - } - - - public void jumpToLineStart() { - evalJavaScript("editor.execCommand('goLineStart');"); - } - - public void jumpToLineEnd() { - evalJavaScript("editor.execCommand('goLineEnd')"); - } - - public void jumpToStart() { - evalJavaScript("editor.execCommand('goDocStart');"); - } - - public void jumpToEnd() { - evalJavaScript("editor.execCommand('goDocEnd');"); - } - - - public void showType() { - evalJavaScript("editor.tern.showType();"); - } - - public void rename() { - evalJavaScript("editor.tern.rename();"); - } - - public void selectName() { - evalJavaScript("editor.tern.selectName();"); - } - - - public void replace(String keywords, String replacement, boolean usingRegex) { - setQuery(keywords, usingRegex); - replacement = replacement.replace("'", "\'"); - evalJavaScript("editor.state.search.text = '" + replacement + "';"); - findNext(); - } - - public Observable getSelection() { - return evalString("editor.getSelection()"); - } - - private Observable evalString(String expr) { - PublishSubject publishSubject = PublishSubject.create(); - int id = SharedVariables.put(publishSubject); - evalJavaScript(String.format(Locale.getDefault(), "__bridge__.setStringFromJs(%d, %s);", id, expr)); - return publishSubject; - } - - public void replaceSelection() { - evalJavaScript("editor.execCommand('replace');"); - } - - private void evalJavaScript(String script) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - mWebView.evaluateJavascript(script, null); - } else { - mWebView.loadUrl("javascript:" + script); - } - } - - public void replace(String text, int fromLine, int fromCh, int toLine, int toCh) { - String js = "editor.replaceRange('%s', {line: %d, ch: %d}, {line: %d, ch: %d})"; - evalJavaScript(String.format(Locale.getDefault(), js, text, fromLine, fromCh, toLine, toCh)); - } - - public class JavaScriptBridge { - - @JavascriptInterface - public void setStringFromJs(int id, String text) { - PublishSubject publishSubject = SharedVariables.remove(id); - if (publishSubject == null) - return; - publishSubject.onNext(text); - publishSubject.onComplete(); - } - - @JavascriptInterface - public void setIntFromJs(int id, int i) { - PublishSubject publishSubject = SharedVariables.remove(id); - if (publishSubject == null) - return; - publishSubject.onNext(i); - publishSubject.onComplete(); - } - - @JavascriptInterface - public String getStringFromAndroid(int id) { - return SharedVariables.remove(id); - } - - @JavascriptInterface - public void onKeyUp(String line, int cursor) { - if (mCallback == null) { - return; - } - mWebView.post(() -> mCallback.onKeyUp(line, cursor)); - } - - } - - private class MyWebViewClient extends WebViewClient { - - private final String INIT_SCRIPT = "editor.on('change', function(){__bridge__.onTextChange();});"; - - @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); - mPageFinished.resolve(null); - evalJavaScript(INIT_SCRIPT); - setProgress(false); - } - - } - - private class MyWebChromeClient extends WebChromeClient { - - private final Pattern RENAME = Pattern.compile("New name for ([a-zA-Z]+)"); - - @Override - public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { - Matcher m = RENAME.matcher(message); - if (m.matches() && m.groupCount() >= 1) { - showRenamePrompt(m.group(1), defaultValue, result); - return true; - } - return super.onJsPrompt(view, url, message, defaultValue, result); - } - - private void showRenamePrompt(String name, String defaultValue, final JsPromptResult result) { - new ThemeColorMaterialDialogBuilder(getContext()) - .title(getResources().getString(R.string.text_rename) + name) - .input("", defaultValue, (dialog, input) -> result.confirm(input.toString())) - .show(); - } - - @Override - public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { - new ThemeColorMaterialDialogBuilder(getContext()) - .title(R.string.text_alert) - .content(message) - .onPositive((dialog, which) -> result.confirm()) - .cancelListener(dialog -> result.cancel()) - .positiveText(R.string.ok) - .show(); - return true; - } - - @Override - public void onProgressChanged(WebView view, int newProgress) { - setProgress(newProgress != 100); - } - - @Override - public boolean onConsoleMessage(ConsoleMessage m) { - switch (m.messageLevel()) { - case LOG: - Log.i(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber())); - return true; - case TIP: - Log.v(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber())); - return true; - case DEBUG: - Log.d(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber())); - return true; - case WARNING: - Log.w(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber())); - return true; - case ERROR: - Log.e(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber())); - return true; - } - return false; - } - } - -} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorActionModeCallback.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorActionModeCallback.java deleted file mode 100644 index 1babcb8d..00000000 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorActionModeCallback.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.stardust.scriptdroid.ui.edit; - -import android.app.Activity; -import android.content.Intent; -import android.os.Build; -import android.support.v7.view.menu.MenuBuilder; -import android.support.v7.view.menu.MenuItemImpl; -import android.util.Log; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; - -import com.stardust.scriptdroid.R; - -/** - * Created by Stardust on 2017/11/2. - */ - -public class EditorActionModeCallback implements ActionMode.Callback { - - - private static final int ID_SELECT_ALL = 34275489; - - private ActionMode.Callback mCallback; - private CodeMirrorEditor mEditor; - - public EditorActionModeCallback(ActionMode.Callback callback, CodeMirrorEditor editor) { - mCallback = callback; - mEditor = editor; - } - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - return mCallback.onCreateActionMode(mode, menu); - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return mCallback.onPrepareActionMode(mode, menu); - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch (item.getItemId()) { - case R.id.action_delete_line: - mEditor.deleteLine(); - return true; - case R.id.action_copy_line: - mEditor.copyLine(); - mode.finish(); - return true; - } - return mCallback.onActionItemClicked(mode, item); - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - mCallback.onDestroyActionMode(mode); - - } -} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorMenu.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorMenu.java index c73a7e49..19e9b0e5 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorMenu.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorMenu.java @@ -5,12 +5,11 @@ import android.support.design.widget.Snackbar; import android.text.InputType; import android.view.MenuItem; -import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.pio.PFiles; import com.stardust.scriptdroid.R; -import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.ui.build.BuildActivity; import com.stardust.scriptdroid.ui.build.BuildActivity_; +import com.stardust.scriptdroid.ui.edit.editor.CodeEditor; import com.stardust.scriptdroid.ui.log.LogActivity_; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.util.ClipboardUtil; @@ -20,7 +19,6 @@ import java.util.Locale; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.annotations.NonNull; -import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; /** @@ -31,12 +29,12 @@ public class EditorMenu { private EditorView mEditorView; private Context mContext; - private CodeMirrorEditor mEditor; + private CodeEditor mEditor; public EditorMenu(EditorView editorView) { mEditorView = editorView; mContext = editorView.getContext(); - mEditor = editorView.mEditor; + mEditor = editorView.getEditor(); } public boolean onOptionsItemSelected(MenuItem item) { @@ -69,9 +67,6 @@ public class EditorMenu { case R.id.action_jump_to_line: jumpToLine(); return true; - case R.id.action_jump_to_def: - mEditor.jumpToDef(); - return true; case R.id.action_jump_to_start: mEditor.jumpToStart(); return true; @@ -94,10 +89,10 @@ public class EditorMenu { beautifyCode(); return true; case R.id.action_rename: - mEditor.rename(); + //mEditor.rename(); return true; case R.id.action_select_variable: - mEditor.selectName(); + //mEditor.selectName(); return true; } return false; @@ -110,7 +105,7 @@ public class EditorMenu { showConsole(); return true; case R.id.action_show_type: - mEditor.showType(); + // mEditor.showType(); return true; case R.id.action_editor_theme: mEditorView.selectEditorTheme(); @@ -212,14 +207,11 @@ public class EditorMenu { private void findOrReplace() { mEditor.getSelection() .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Consumer() { - @Override - public void accept(@NonNull String s) throws Exception { + .subscribe(s -> new FindOrReplaceDialogBuilder(mContext, mEditorView) .setQueryIfNotEmpty(s) - .show(); - } - }); + .show() + ); } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorView.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorView.java index b7423a95..51199ded 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditorView.java @@ -5,7 +5,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.widget.DrawerLayout; @@ -17,7 +16,6 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Toast; -import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.autojs.engine.JavaScriptEngine; import com.stardust.autojs.execution.ScriptExecution; import com.stardust.pio.PFiles; @@ -31,10 +29,11 @@ import com.stardust.scriptdroid.model.script.Scripts; import com.stardust.scriptdroid.ui.doc.ManualDialog; import com.stardust.scriptdroid.model.autocomplete.CodeCompletions; import com.stardust.scriptdroid.ui.edit.completion.CodeCompletionBar; -import com.stardust.scriptdroid.ui.edit.completion.InputMethodEnhancedBarColors; import com.stardust.scriptdroid.model.autocomplete.Symbols; +import com.stardust.scriptdroid.ui.edit.editor.CodeEditor; import com.stardust.scriptdroid.ui.edit.keyboard.FunctionsKeyboardHelper; import com.stardust.scriptdroid.ui.edit.keyboard.FunctionsKeyboardView; +import com.stardust.scriptdroid.ui.edit.theme.Theme; import com.stardust.scriptdroid.ui.log.LogActivity_; import com.stardust.scriptdroid.ui.widget.EWebView; import com.stardust.scriptdroid.ui.widget.ToolbarMenuItem; @@ -47,7 +46,6 @@ import org.androidannotations.annotations.EViewGroup; import org.androidannotations.annotations.ViewById; import java.io.File; -import java.util.Arrays; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -69,7 +67,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC public static final String EXTRA_RUN_ENABLED = "Love you with my life...really...17.9.28"; @ViewById(R.id.editor) - CodeMirrorEditor mEditor; + CodeEditor mEditor; @ViewById(R.id.code_completion_bar) CodeCompletionBar mCodeCompletionBar; @@ -104,9 +102,8 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC private File mFile; private boolean mReadOnly = false; private ScriptExecution mScriptExecution; - private boolean mTextChanged = false; - private boolean mInitialText = false; private AutoCompletion mAutoCompletion; + private Theme mEditorTheme; private FunctionsKeyboardHelper mFunctionsKeyboardHelper; private BroadcastReceiver mOnRunFinishedReceiver = new BroadcastReceiver() { @Override @@ -209,9 +206,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC } private void setInitialText(String text) { - mInitialText = true; - mTextChanged = false; - mEditor.setText(text); + mEditor.setInitialText(text); } @@ -222,6 +217,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC @AfterViews void init() { + setTheme(Theme.getDefault(getContext())); setUpEditor(); setUpInputMethodEnhancedBar(); setUpFunctionsKeyboard(); @@ -236,7 +232,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC .setContent(mEditor) .setFunctionsTrigger(mShowFunctionsButton) .setFunctionsView(mFunctionsKeyboard) - .setEditView(mEditor.getWebView()) + .setEditView(mEditor.getCodeEditText()) .build(); mFunctionsKeyboard.setClickCallback(this); } @@ -251,16 +247,9 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC private void setUpEditor() { - setTheme(PreferenceManager.getDefaultSharedPreferences(getContext()) - .getString(KEY_EDITOR_THEME, mEditor.getTheme())); - mEditor.setCallback((line, cursor) -> { - if (mInitialText) { - mInitialText = false; - } else { - mTextChanged = true; - setMenuItemStatus(R.id.save, true); - autoComplete(line, cursor); - } + mEditor.setCursorChangeCallback((line, cursor) -> { + setMenuItemStatus(R.id.save, mEditor.isTextChanged()); + autoComplete(line, cursor); }); @@ -270,10 +259,11 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC mAutoCompletion.onCursorChange(line, cursor); } - public void setTheme(String theme) { + public void setTheme(Theme theme) { + mEditorTheme = theme; mEditor.setTheme(theme); - mInputMethodEnhanceBar.setBackgroundColor(InputMethodEnhancedBarColors.getBackgroundColor(theme)); - int textColor = InputMethodEnhancedBarColors.getTextColor(theme); + mInputMethodEnhanceBar.setBackgroundColor(theme.getImeBarBackgroundColor()); + int textColor = theme.getImeBarForegroundColor(); mCodeCompletionBar.setTextColor(textColor); mSymbolBar.setTextColor(textColor); mShowFunctionsButton.setColorFilter(textColor); @@ -321,7 +311,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC .doOnNext(s -> PFiles.write(mFile, s)) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(s -> { - mTextChanged = false; + mEditor.markTextAsSaved(); setMenuItemStatus(R.id.save, false); }); } @@ -362,7 +352,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC } public boolean isTextChanged() { - return mTextChanged; + return mEditor.isTextChanged(); } public void showConsole() { @@ -381,22 +371,10 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC } public void selectEditorTheme() { - String[] themes = mEditor.getAvailableThemes(); - int i = Arrays.asList(themes).indexOf(mEditor.getTheme()); - new MaterialDialog.Builder(getContext()) - .title(R.string.text_editor_theme) - .items((CharSequence[]) themes) - .itemsCallbackSingleChoice(i, (dialog, itemView, which, text) -> { - PreferenceManager.getDefaultSharedPreferences(getContext()).edit() - .putString(KEY_EDITOR_THEME, text.toString()) - .apply(); - setTheme(text.toString()); - return true; - }) - .show(); + } - public CodeMirrorEditor getEditor() { + public CodeEditor getEditor() { return mEditor; } @@ -466,7 +444,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC mEditor.insert(m.getName() + "." + p); } if (!property.isVariable()) { - mEditor.moveCursor(0, -1); + mEditor.moveCursor(-1); } mFunctionsKeyboardHelper.hideFunctionsLayout(true); } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/InputConnectProxy.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/InputConnectProxy.java deleted file mode 100644 index d1eabbd4..00000000 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/InputConnectProxy.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.stardust.scriptdroid.ui.edit; - -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresApi; -import android.view.KeyEvent; -import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.CorrectionInfo; -import android.view.inputmethod.ExtractedText; -import android.view.inputmethod.ExtractedTextRequest; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputContentInfo; - -/** - * Created by Stardust on 2017/11/4. - */ - -public class InputConnectProxy implements InputConnection { - - private InputConnection mInputConnection; - - public InputConnectProxy(InputConnection inputConnection) { - mInputConnection = inputConnection; - } - - @Override - public CharSequence getTextBeforeCursor(int n, int flags) { - return mInputConnection.getTextBeforeCursor(n, flags); - } - - @Override - public CharSequence getTextAfterCursor(int n, int flags) { - return mInputConnection.getTextAfterCursor(n, flags); - } - - @Override - public CharSequence getSelectedText(int flags) { - return mInputConnection.getSelectedText(flags); - } - - @Override - public int getCursorCapsMode(int reqModes) { - return mInputConnection.getCursorCapsMode(reqModes); - } - - @Override - public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { - return mInputConnection.getExtractedText(request, flags); - } - - @Override - public boolean deleteSurroundingText(int beforeLength, int afterLength) { - return mInputConnection.deleteSurroundingText(beforeLength, afterLength); - } - - @RequiresApi(api = Build.VERSION_CODES.N) - @Override - public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { - return mInputConnection.deleteSurroundingTextInCodePoints(beforeLength, afterLength); - } - - @Override - public boolean setComposingText(CharSequence text, int newCursorPosition) { - return mInputConnection.setComposingText(text, newCursorPosition); - } - - @Override - public boolean setComposingRegion(int start, int end) { - return mInputConnection.setComposingRegion(start, end); - } - - @Override - public boolean finishComposingText() { - return mInputConnection.finishComposingText(); - } - - @Override - public boolean commitText(CharSequence text, int newCursorPosition) { - return mInputConnection.commitText(text, newCursorPosition); - } - - @Override - public boolean commitCompletion(CompletionInfo text) { - return mInputConnection.commitCompletion(text); - } - - @Override - public boolean commitCorrection(CorrectionInfo correctionInfo) { - return mInputConnection.commitCorrection(correctionInfo); - } - - @Override - public boolean setSelection(int start, int end) { - return mInputConnection.setSelection(start, end); - } - - @Override - public boolean performEditorAction(int editorAction) { - return mInputConnection.performEditorAction(editorAction); - } - - @Override - public boolean performContextMenuAction(int id) { - return mInputConnection.performContextMenuAction(id); - } - - @Override - public boolean beginBatchEdit() { - return mInputConnection.beginBatchEdit(); - } - - @Override - public boolean endBatchEdit() { - return mInputConnection.endBatchEdit(); - } - - @Override - public boolean sendKeyEvent(KeyEvent event) { - return mInputConnection.sendKeyEvent(event); - } - - @Override - public boolean clearMetaKeyStates(int states) { - return mInputConnection.clearMetaKeyStates(states); - } - - @Override - public boolean reportFullscreenMode(boolean enabled) { - return mInputConnection.reportFullscreenMode(enabled); - } - - @Override - public boolean performPrivateCommand(String action, Bundle data) { - return mInputConnection.performPrivateCommand(action, data); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @Override - public boolean requestCursorUpdates(int cursorUpdateMode) { - return mInputConnection.requestCursorUpdates(cursorUpdateMode); - } - - @RequiresApi(api = Build.VERSION_CODES.N) - @Override - public Handler getHandler() { - return mInputConnection.getHandler(); - } - - @RequiresApi(api = Build.VERSION_CODES.N) - @Override - public void closeConnection() { - mInputConnection.closeConnection(); - } - - @RequiresApi(api = Build.VERSION_CODES.N_MR1) - @Override - public boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags, @Nullable Bundle opts) { - return mInputConnection.commitContent(inputContentInfo, flags, opts); - } -} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/SharedVariables.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/SharedVariables.java deleted file mode 100644 index 5c0f9bf6..00000000 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/SharedVariables.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.stardust.scriptdroid.ui.edit; - -import android.util.SparseArray; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created by Stardust on 2018/2/4. - */ - -public class SharedVariables { - - private static SparseArray sSharedVariables = new SparseArray<>(); - private static AtomicInteger sMaxId = new AtomicInteger(); - - - public static int put(Object value) { - int id = sMaxId.getAndIncrement(); - sSharedVariables.put(id, value); - return id; - } - - @SuppressWarnings("unchecked") - public static T remove(int id) { - Object o = sSharedVariables.get(id); - if (o != null) - sSharedVariables.remove(id); - return (T) o; - } - -} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/InputMethodEnhancedBarColors.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/InputMethodEnhancedBarColors.java deleted file mode 100644 index b2356da5..00000000 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/InputMethodEnhancedBarColors.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.stardust.scriptdroid.ui.edit.completion; - -import android.graphics.Color; - -import com.stardust.util.MapEntries; - -import java.util.Map; - -/** - * Created by Stardust on 2017/9/28. - */ - -public class InputMethodEnhancedBarColors { - - private static final Map sEditorThemeColors = new MapEntries() - .entry("3024-day", 0xf7f7f7) - .entry("3024-night", 0x090300) - .entry("abcdef", 0x0f0f0f) - .entry("ambiance-mobile", 0xffffff) - .entry("ambiance", 0xffffff) - .entry("base16-dark", 0x151515) - .entry("base16-light", 0xf5f5f5) - .entry("bespin", 0x28211c) - .entry("dracula", 0x282a36) - .entry("duotone-dark", 0x2a2734) - .entry("duotone-light", 0xfaf8f5) - .entry("eclipse", 0xffffff) - .entry("icecoder", 0x1d1d1b) - .entry("material", 0x263238) - .entry("mbo", 0x2c2c2c) - .entry("mdn-like", 0xfefefe) - .entry("monokai", 0x272822) - .entry("neat", 0xffffff) - .entry("neo", 0xffffff) - .entry("night", 0x0a001f) - .entry("panda-syntax", 0x292a2b) - .entry("paraiso-dark", 0x2f1e2e) - .entry("paraiso-light", 0xe7e9db) - .entry("pastel-on-dark", 0x2c2827) - .entry("railscasts", 0x2b2b2b) - .entry("rubyblue", 0x112435) - .entry("seti", 0x151718) - .entry("solarized", 0xffffff) - .entry("the-matrix", 0x000000) - .entry("tomorrow-night-bright", 0x000000) - .entry("tomorrow-night-eighties", 0x000000) - .entry("ttcn", 0xffffff) - .entry("twilight", 0x141414) - .entry("xq-light", 0xffffff) - .entry("zenburn", 0x3f3f3f) - .map(); - - public static int getBackgroundColor(String theme) { - int themeColor = sEditorThemeColors.get(theme); - if (themeColor == 0xffffff) { - return 0x828389; - } - return themeColor | 0xdd000000; - } - - public static int getTextColor(String theme) { - int c = sEditorThemeColors.get(theme); - if (isBrightColor(c)) { - return Color.BLACK; - } else { - return Color.WHITE; - } - } - - public static boolean isBrightColor(int color) { - int[] rgb = {Color.red(color), Color.green(color), Color.blue(color)}; - int brightness = (int) Math.sqrt(rgb[0] * rgb[0] * .241 + rgb[1] - * rgb[1] * .691 + rgb[2] * rgb[2] * .068); - return brightness >= 200; - } - -} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/CodeEditText.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/CodeEditText.java new file mode 100644 index 00000000..aa87dbd1 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/CodeEditText.java @@ -0,0 +1,212 @@ +/** + * Copyright 2018 WHO<980008027@qq.com> + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *

+ * Modified by project: https://github.com/980008027/JsDroidEditor + */ +package com.stardust.scriptdroid.ui.edit.editor; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.support.v7.widget.AppCompatEditText; +import android.text.Editable; +import android.text.Layout; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; + +import com.stardust.scriptdroid.ui.edit.theme.Theme; + +/** + * Created by Administrator on 2018/2/11. + */ + +public class CodeEditText extends AppCompatEditText { + + + private static final String LOG_TAG = "CodeEditText"; + // 文字范围 + protected HVScrollView mScrollView; + + private CodeEditor.CursorChangeCallback mCallback; + private volatile JavaScriptHighlighter.HighlightTokens mHighlightTokens; + private Theme mTheme; + + public CodeEditText(Context context) { + super(context); + init(); + } + + public CodeEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + setGravity(Gravity.START); + // 设值背景透明 + setBackgroundColor(Color.TRANSPARENT); + // 设置字体颜色 + setTextColor(Color.TRANSPARENT); + // 设置字体 + setTypeface(Typeface.MONOSPACE); + mTheme = Theme.getDefault(getContext()); + } + + public void setTheme(Theme theme) { + mTheme = theme; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mScrollView == null) { + mScrollView = (HVScrollView) getParent(); + } + // 根据行号计算左边距padding + String max = Integer.toString(getLineCount()); + float lineNumberSize = getPaint().measureText(max) + 20; + if (getPaddingLeft() != lineNumberSize) { + setPadding((int) lineNumberSize, 0, 0, 0); + invalidate(); + } + super.onDraw(canvas); + // 画文字 + canvas.save(); + canvas.translate(0, getExtendedPaddingTop()); + drawText(canvas); + canvas.restore(); + + } + + // 绘制文本着色 + private void drawText(Canvas canvas) { + JavaScriptHighlighter.HighlightTokens highlightTokens = mHighlightTokens; + Layout layout = getLayout(); + long lineRange = getLineRangeForDraw(layout, canvas); + int firstLineForDraw = LayoutHelper.unpackRangeStartFromLong(lineRange); + int lastLineForDraw = LayoutHelper.unpackRangeEndFromLong(lineRange); + if (firstLineForDraw < 0) { + return; + } + int lineCount = getLineCount(); + int paddingLeft = getPaddingLeft(); + Paint paint = getPaint(); + for (int line = firstLineForDraw; line <= lastLineForDraw && line < lineCount; line++) { + int lineBottom = layout.getLineTop(line + 1); + int lineBaseline = lineBottom - layout.getLineDescent(line); + + drawLineNumber(canvas, paint, line, lineBaseline); + if (highlightTokens == null) + continue; + + drawCode(canvas, paint, paddingLeft, line, layout, lineBaseline, highlightTokens); + + } + } + + private void drawCode(Canvas canvas, Paint paint, int paddingLeft, int line, Layout layout, int lineBaseline, JavaScriptHighlighter.HighlightTokens highlightTokens) { + int lineStart = layout.getLineStart(line); + if (lineStart >= mHighlightTokens.getText().length()) { + return; + } + int lineEnd = layout.getLineVisibleEnd(line); + int fontCount = 0; + int previousColor = mHighlightTokens.getCharColor(lineStart); + int previousColorPos = lineStart; + for (int i = lineStart; i < lineEnd; i++) { + fontCount++; + int color = mHighlightTokens.getCharColor(i); + if (previousColor != color) { + drawText(canvas, paint, paddingLeft, lineBaseline, lineStart, previousColorPos, previousColorPos + fontCount, previousColor); + previousColor = color; + previousColorPos = i; + fontCount = 1; + } + if (i == lineEnd - 1) { + drawText(canvas, paint, paddingLeft, lineBaseline, lineStart, previousColorPos, previousColorPos + fontCount, previousColor); + } + } + + } + + private void drawText(Canvas canvas, Paint paint, int paddingLeft, int lineBaseline, int lineStart, int start, int end, int color) { + if (start >= end) { + return; + } + paint.setColor(color); + float offsetX = paint.measureText(getText(), lineStart, start); + canvas.drawText(getText(), start, end, paddingLeft + offsetX, lineBaseline, paint); + } + + private void drawLineNumber(Canvas canvas, Paint paint, int line, int lineBaseline) { + String lineNumberText = Integer.toString(line + 1); + paint.setColor(mTheme.getLineNumberColor()); + canvas.drawText(lineNumberText, 0, lineNumberText.length(), 10, + lineBaseline, paint); + } + + private long getLineRangeForDraw(Layout layout, Canvas canvas) { + canvas.save(); + float clipTop = (mScrollView.getScrollY() == 0) ? 0 + : getExtendedPaddingTop() + mScrollView.getScrollY() + - mScrollView.getPaddingTop(); + canvas.clipRect(0, clipTop, getWidth(), mScrollView.getScrollY() + + mScrollView.getHeight()); + long lineRangeForDraw = LayoutHelper.getLineRangeForDraw(layout, canvas); + canvas.restore(); + return lineRangeForDraw; + } + + @Override + protected void onSelectionChanged(int selStart, int selEnd) { + super.onSelectionChanged(selStart, selEnd); + if (mCallback == null || selStart != selEnd) { + return; + } + String text = getText().toString(); + if (text.isEmpty()) { + return; + } + int lineStart = text.lastIndexOf("\n", selStart) + 1; + if (lineStart < 0) { + lineStart = 0; + } + if (lineStart > text.length() - 1) { + lineStart = text.length() - 1; + } + int lineEnd = text.indexOf("\n", selStart); + if (lineEnd < 0) { + lineEnd = text.length(); + } + if (lineEnd < lineStart || lineStart < 0 || lineEnd > text.length()) + return; + String line = text.substring(lineStart, lineEnd); + int cursor = selStart - lineStart; + mCallback.onCursorChange(line, cursor); + } + + public void setCursorChangeCallback(CodeEditor.CursorChangeCallback callback) { + mCallback = callback; + } + + public void updateHighlightTokens(JavaScriptHighlighter.HighlightTokens highlightTokens) { + mHighlightTokens = highlightTokens; + Log.d(LOG_TAG, "tokens = " + highlightTokens); + invalidate(); + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/CodeEditor.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/CodeEditor.java new file mode 100644 index 00000000..54053242 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/CodeEditor.java @@ -0,0 +1,277 @@ +package com.stardust.scriptdroid.ui.edit.editor; + +import android.content.Context; +import android.support.design.widget.Snackbar; +import android.util.AttributeSet; + +import com.stardust.scriptdroid.R; +import com.stardust.scriptdroid.ui.edit.theme.Theme; +import com.stardust.util.ClipboardUtil; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import io.reactivex.Observable; + +/** + * Copyright 2018 WHO<980008027@qq.com> + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *

+ * Modified by project: https://github.com/980008027/JsDroidEditor + */ +public class CodeEditor extends HVScrollView { + + + private CharSequence mReplacement = ""; + private String mKeywords; + private int mFoundIndex = -1; + + public Observable getLineCount() { + return Observable.just(mCodeEditText.getLayout().getLineCount()); + } + + public void copyLine() { + int line = getLineOfChar(mCodeEditText.getSelectionStart()); + if (line < 0 || line >= mCodeEditText.getLayout().getLineCount()) + return; + CharSequence lineText = mCodeEditText.getText().subSequence(mCodeEditText.getLayout().getLineStart(line), + mCodeEditText.getLayout().getLineEnd(line)); + ClipboardUtil.setClip(getContext(), lineText); + Snackbar.make(this, R.string.text_already_copy_to_clip, Snackbar.LENGTH_SHORT).show(); + } + + private int getLineOfChar(int i) { + int low = 0; + int high = mCodeEditText.getLineCount() - 1; + while (low < high) { + int mid = (low + high) >>> 1; + int midVal = mCodeEditText.getLayout().getLineEnd(mid); + + if (midVal <= i) + low = mid + 1; + else if (midVal > i) + high = mid - 1; + } + return low; + } + + public void deleteLine() { + int line = getLineOfChar(mCodeEditText.getSelectionStart()); + if (line < 0 || line >= mCodeEditText.getLayout().getLineCount()) + return; + mCodeEditText.getText().replace(mCodeEditText.getLayout().getLineStart(line), + mCodeEditText.getLayout().getLineEnd(line), ""); + } + + public void jumpToStart() { + mCodeEditText.setSelection(0); + } + + public void jumpToEnd() { + mCodeEditText.setSelection(mCodeEditText.getText().length()); + } + + public void jumpToLineStart() { + int line = getLineOfChar(mCodeEditText.getSelectionStart()); + if (line < 0 || line >= mCodeEditText.getLayout().getLineCount()) + return; + mCodeEditText.setSelection(mCodeEditText.getLayout().getLineStart(line)); + } + + public void jumpToLineEnd() { + int line = getLineOfChar(mCodeEditText.getSelectionStart()); + if (line < 0 || line >= mCodeEditText.getLayout().getLineCount()) + return; + mCodeEditText.setSelection(mCodeEditText.getLayout().getLineEnd(line) - 1); + + } + + + public interface CursorChangeCallback { + + void onCursorChange(String line, int ch); + + } + + private CodeEditText mCodeEditText; + private PreformEdit mPreformEdit; + private JavaScriptHighlighter mJavaScriptHighlighter; + private Theme mTheme; + + public CodeEditor(Context context) { + super(context); + init(); + } + + public CodeEditor(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + + private void init() { + setFillViewport(true); + inflate(getContext(), R.layout.code_editor, this); + mCodeEditText = (CodeEditText) findViewById(R.id.code_edit_text); + mPreformEdit = new PreformEdit(mCodeEditText); + mJavaScriptHighlighter = new JavaScriptHighlighter(mTheme, mCodeEditText); + setTheme(Theme.getDefault(getContext())); + + } + + public void setTheme(Theme theme) { + mTheme = theme; + mJavaScriptHighlighter.setTheme(theme); + setBackgroundColor(mTheme.getBackgroundColor()); + + } + + + public boolean isTextChanged() { + return mPreformEdit.isTextChanged(); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + mCodeEditText.postInvalidate(); + } + + public CodeEditText getCodeEditText() { + return mCodeEditText; + } + + public void setInitialText(String text) { + mCodeEditText.setText(text); + mPreformEdit.setDefaultText(text); + } + + public void jumpTo(int line, int col) { + mCodeEditText.setSelection(mCodeEditText.getLayout().getLineStart(line) + col); + //int top = mCodeEditText.getLayout().getLineTop(line - 1); + //post(() -> scrollTo(0, top)); + + } + + public void setReadOnly(boolean readOnly) { + setEnabled(!readOnly); + } + + public void setProgress(boolean progress) { + + } + + public void setText(String text) { + mCodeEditText.setText(text); + } + + public void setCursorChangeCallback(CursorChangeCallback callback) { + mCodeEditText.setCursorChangeCallback(callback); + } + + + public void undo() { + mPreformEdit.undo(); + } + + public void redo() { + mPreformEdit.redo(); + } + + + public void findNext() { + Matcher matcher = Pattern.compile(mKeywords).matcher(mCodeEditText.getText()); + if (!matcher.find(mFoundIndex + 1)) { + return; + } + mFoundIndex = matcher.start(); + mCodeEditText.setSelection(mFoundIndex, mFoundIndex + mKeywords.length()); + } + + public void findPrev() { + // FIXME: 2018/2/21 lastIndexOf不支持正则 + if (mFoundIndex <= 0) { + mFoundIndex = mCodeEditText.getText().length(); + } + int index = mCodeEditText.getText().toString().lastIndexOf(mKeywords, mFoundIndex - 1); + if (index < 0) { + return; + } + mFoundIndex = index; + mCodeEditText.setSelection(index, index + mKeywords.length()); + } + + public void replaceSelection() { + mCodeEditText.getText().replace(mCodeEditText.getSelectionStart(), mCodeEditText.getSelectionEnd(), mReplacement); + } + + public void beautifyCode() { + + } + + public void find(String keywords, boolean usingRegex) { + if (usingRegex) { + mKeywords = keywords; + } else { + mKeywords = Pattern.quote(keywords); + } + mFoundIndex = -1; + findNext(); + } + + public void replace(String keywords, String replacement, boolean usingRegex) { + mReplacement = replacement == null ? "" : replacement; + find(keywords, usingRegex); + } + + public void replaceAll(String keywords, String replacement, boolean usingRegex) { + if (!usingRegex) { + keywords = Pattern.quote(keywords); + } + String text = mCodeEditText.getText().toString(); + text = text.replaceAll(keywords, replacement); + setText(text); + } + + public void insert(String insertText) { + int selection = Math.max(mCodeEditText.getSelectionStart(), 0); + mCodeEditText.getText().insert(selection, insertText); + } + + public void moveCursor(int dCh) { + mCodeEditText.setSelection(mCodeEditText.getSelectionStart() + dCh); + } + + public Observable getText() { + return Observable.just(mCodeEditText.getText().toString()); + } + + public Observable getSelection() { + int s = mCodeEditText.getSelectionStart(); + int e = mCodeEditText.getSelectionEnd(); + if (s == e) { + return Observable.just(""); + } + return Observable.just(mCodeEditText.getText().toString().substring(s, e)); + } + + + public void markTextAsSaved() { + mPreformEdit.clearHistory(); + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/HVScrollView.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/HVScrollView.java new file mode 100644 index 00000000..cecb126d --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/HVScrollView.java @@ -0,0 +1,1564 @@ +package com.stardust.scriptdroid.ui.edit.editor; + +import java.util.List; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.FocusFinder; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewGroup.MarginLayoutParams; +import android.view.ViewParent; +import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; +import android.widget.Scroller; + + +/** + * Reference to ScrollView and HorizontalScrollView + */ +public class HVScrollView extends FrameLayout { + static final int ANIMATED_SCROLL_GAP = 250; + + static final float MAX_SCROLL_FACTOR = 0.5f; + + + private long mLastScroll; + + private final Rect mTempRect = new Rect(); + private Scroller mScroller; + + /** + * Flag to indicate that we are moving focus ourselves. This is so the + * code that watches for focus changes initiated outside this ScrollView + * knows that it does not have to do anything. + */ + private boolean mScrollViewMovedFocus; + + /** + * Position of the last motion event. + */ + private float mLastMotionY; + private float mLastMotionX; + + /** + * True when the layout has changed but the traversal has not come through yet. + * Ideally the view hierarchy would keep track of this for us. + */ + private boolean mIsLayoutDirty = true; + + /** + * The child to give focus to in the event that a child has requested focus while the + * layout is dirty. This prevents the scroll from being wrong if the child has not been + * laid out before requesting focus. + */ + private View mChildToScrollTo = null; + + /** + * True if the user is currently dragging this ScrollView around. This is + * not the same as 'is being flinged', which can be checked by + * mScroller.isFinished() (flinging begins when the user lifts his finger). + */ + private boolean mIsBeingDragged = false; + + /** + * Determines speed during touch scrolling + */ + private VelocityTracker mVelocityTracker; + + /** + * When set to true, the scroll view measure its child to make it fill the currently + * visible area. + */ + private boolean mFillViewport; + + /** + * Whether arrow scrolling is animated. + */ + private boolean mSmoothScrollingEnabled = true; + + private int mTouchSlop; + private int mMinimumVelocity; + private int mMaximumVelocity; + + /** + * ID of the active pointer. This is used to retain consistency during + * drags/flings if multiple pointers are used. + */ + private int mActivePointerId = INVALID_POINTER; + + /** + * Sentinel value for no current active pointer. + * Used by {@link #mActivePointerId}. + */ + private static final int INVALID_POINTER = -1; + + private boolean mFlingEnabled = true; + + public HVScrollView(Context context) { + this(context, null); + } + + public HVScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + initScrollView(); + } + + @Override + protected float getTopFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getVerticalFadingEdgeLength(); + if (getScrollY() < length) { + return getScrollY() / (float) length; + } + + return 1.0f; + } + + @Override + protected float getLeftFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getHorizontalFadingEdgeLength(); + if (getScrollX() < length) { + return getScrollX() / (float) length; + } + + return 1.0f; + } + + @Override + protected float getRightFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getHorizontalFadingEdgeLength(); + final int rightEdge = getWidth() - getPaddingRight(); + final int span = getChildAt(0).getRight() - getScrollX() - rightEdge; + if (span < length) { + return span / (float) length; + } + + return 1.0f; + } + + @Override + protected float getBottomFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getVerticalFadingEdgeLength(); + final int bottomEdge = getHeight() - getPaddingBottom(); + final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge; + if (span < length) { + return span / (float) length; + } + + return 1.0f; + } + + /** + * @return The maximum amount this scroll view will scroll in response to + * an arrow event. + */ + public int getMaxScrollAmountV() { + return (int) (MAX_SCROLL_FACTOR * (getBottom() - getTop())); + } + + public int getMaxScrollAmountH() { + return (int) (MAX_SCROLL_FACTOR * (getRight() - getLeft())); + } + + + private void initScrollView() { + mScroller = new Scroller(getContext()); + setFocusable(true); + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); + setWillNotDraw(false); + final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + } + + @Override + public void addView(View child) { + if (getChildCount() > 0) { + throw new IllegalStateException("ScrollView can host only one direct child"); + } + + super.addView(child); + } + + @Override + public void addView(View child, int index) { + if (getChildCount() > 0) { + throw new IllegalStateException("ScrollView can host only one direct child"); + } + + super.addView(child, index); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (getChildCount() > 0) { + throw new IllegalStateException("ScrollView can host only one direct child"); + } + + super.addView(child, params); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (getChildCount() > 0) { + throw new IllegalStateException("ScrollView can host only one direct child"); + } + + super.addView(child, index, params); + } + + /** + * @return Returns true this ScrollView can be scrolled + */ + private boolean canScrollV() { + View child = getChildAt(0); + if (child != null) { + int childHeight = child.getHeight(); + return getHeight() < childHeight + getPaddingTop() + getPaddingBottom(); + } + return false; + } + + private boolean canScrollH() { + View child = getChildAt(0); + if (child != null) { + int childWidth = child.getWidth(); + return getWidth() < childWidth + getPaddingLeft() + getPaddingRight(); + } + return false; + } + + /** + * Indicates whether this ScrollView's content is stretched to fill the viewport. + * + * @return True if the content fills the viewport, false otherwise. + */ + public boolean isFillViewport() { + return mFillViewport; + } + + /** + * Indicates this ScrollView whether it should stretch its content height to fill + * the viewport or not. + * + * @param fillViewport True to stretch the content's height to the viewport's + * boundaries, false otherwise. + */ + public void setFillViewport(boolean fillViewport) { + if (fillViewport != mFillViewport) { + mFillViewport = fillViewport; + requestLayout(); + } + } + + /** + * @return Whether arrow scrolling will animate its transition. + */ + public boolean isSmoothScrollingEnabled() { + return mSmoothScrollingEnabled; + } + + /** + * Set whether arrow scrolling will animate its transition. + * + * @param smoothScrollingEnabled whether arrow scrolling will animate its transition + */ + public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) { + mSmoothScrollingEnabled = smoothScrollingEnabled; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (!mFillViewport) { + return; + } + + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + if (heightMode == MeasureSpec.UNSPECIFIED && widthMode == MeasureSpec.UNSPECIFIED) { + return; + } + + if (getChildCount() > 0) { + final View child = getChildAt(0); + int height = getMeasuredHeight(); + int width = getMeasuredWidth(); + if (child.getMeasuredHeight() < height || child.getMeasuredWidth() < width) { + width -= getPaddingLeft(); + width -= getPaddingRight(); + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); + + height -= getPaddingTop(); + height -= getPaddingBottom(); + int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + } + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + // Let the focused view and/or our descendants get the key first + return super.dispatchKeyEvent(event) || executeKeyEvent(event); + } + + /** + * You can call this function yourself to have the scroll view perform + * scrolling from a key event, just as if the event had been dispatched to + * it by the view hierarchy. + * + * @param event The key event to execute. + * @return Return true if the event was handled, else false. + */ + public boolean executeKeyEvent(KeyEvent event) { + mTempRect.setEmpty(); + + boolean handled = false; + + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_DPAD_LEFT: + if (canScrollH()) { + if (!event.isAltPressed()) { + handled = arrowScrollH(View.FOCUS_LEFT); + } else { + handled = fullScrollH(View.FOCUS_LEFT); + } + } + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (canScrollH()) { + if (!event.isAltPressed()) { + handled = arrowScrollH(View.FOCUS_RIGHT); + } else { + handled = fullScrollH(View.FOCUS_RIGHT); + } + } + break; + case KeyEvent.KEYCODE_DPAD_UP: + if (canScrollV()) { + if (!event.isAltPressed()) { + handled = arrowScrollV(View.FOCUS_UP); + } else { + handled = fullScrollV(View.FOCUS_UP); + } + } + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + if (canScrollV()) { + if (!event.isAltPressed()) { + handled = arrowScrollV(View.FOCUS_DOWN); + } else { + handled = fullScrollV(View.FOCUS_DOWN); + } + } + break; + } + } + return handled; + } + + private boolean inChild(int x, int y) { + if (getChildCount() > 0) { + final int scrollX = getScrollX(); + final int scrollY = getScrollY(); + final View child = getChildAt(0); + return !(y < child.getTop() - scrollY + || y >= child.getBottom() - scrollY + || x < child.getLeft() - scrollX + || x >= child.getRight() - scrollX); + } + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + /* + * This method JUST determines whether we want to intercept the motion. + * If we return true, onMotionEvent will be called and we do the actual + * scrolling there. + */ + + /* + * Shortcut the most recurring case: the user is in the dragging + * state and he is moving his finger. We want to intercept this + * motion. + */ + final int action = ev.getAction(); + if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { + return true; + } + + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_MOVE: { + /* + * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check + * whether the user has moved far enough from his original down touch. + */ + + /* + * Locally do absolute value. mLastMotionY is set to the y value + * of the down event. + */ + final int activePointerId = mActivePointerId; + if (activePointerId == INVALID_POINTER) { + // If we don't have a valid id, the touch down wasn't on content. + break; + } + + final int pointerIndex = ev.findPointerIndex(activePointerId); + final float y = ev.getY(pointerIndex); + final int yDiff = (int) Math.abs(y - mLastMotionY); + if (yDiff > mTouchSlop) { + mIsBeingDragged = true; + mLastMotionY = y; + } + final float x = ev.getX(pointerIndex); + final int xDiff = (int) Math.abs(x - mLastMotionX); + if (xDiff > mTouchSlop) { + mIsBeingDragged = true; + mLastMotionX = x; + } + break; + } + + case MotionEvent.ACTION_DOWN: { + final float x = ev.getX(); + final float y = ev.getY(); + if (!inChild((int) x, (int) y)) { + mIsBeingDragged = false; + break; + } + + /* + * Remember location of down touch. + * ACTION_DOWN always refers to pointer index 0. + */ + mLastMotionY = y; + mLastMotionX = x; + mActivePointerId = ev.getPointerId(0); + + /* + * If being flinged and user touches the screen, initiate drag; + * otherwise don't. mScroller.isFinished should be false when + * being flinged. + */ + mIsBeingDragged = !mScroller.isFinished(); + break; + } + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + /* Release the drag */ + mIsBeingDragged = false; + mActivePointerId = INVALID_POINTER; + break; + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; + } + + /* + * The only time we want to intercept motion events is if we are in the + * drag mode. + */ + return mIsBeingDragged; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + + if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { + // Don't handle edge touches immediately -- they may actually belong to one of our + // descendants. + return false; + } + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + + final int action = ev.getAction(); + + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { + final float x = ev.getX(); + final float y = ev.getY(); + if (!(mIsBeingDragged = inChild((int) x, (int) y))) { + return false; + } + + /* + * If being flinged and user touches, stop the fling. isFinished + * will be false if being flinged. + */ + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + + // Remember where the motion event started + mLastMotionY = y; + mLastMotionX = x; + mActivePointerId = ev.getPointerId(0); + break; + } + case MotionEvent.ACTION_MOVE: + if (mIsBeingDragged) { + // Scroll to follow the motion event + final int activePointerIndex = ev.findPointerIndex(mActivePointerId); + final float y = ev.getY(activePointerIndex); + final int deltaY = (int) (mLastMotionY - y); + mLastMotionY = y; + + final float x = ev.getX(activePointerIndex); + final int deltaX = (int) (mLastMotionX - x); + mLastMotionX = x; + + scrollBy(deltaX, deltaY); + } + break; + case MotionEvent.ACTION_UP: + if (mIsBeingDragged) { + if (mFlingEnabled) { + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int initialVelocitx = (int) velocityTracker.getXVelocity(); + int initialVelocity = (int) velocityTracker.getYVelocity(); +// int initialVelocitx = (int) velocityTracker.getXVelocity(mActivePointerId); +// int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); + + if (getChildCount() > 0) { + if (Math.abs(initialVelocitx) > initialVelocitx || Math.abs(initialVelocity) > mMinimumVelocity) { + fling(-initialVelocitx, -initialVelocity); + } + + } + } + + mActivePointerId = INVALID_POINTER; + mIsBeingDragged = false; + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + break; + case MotionEvent.ACTION_CANCEL: + if (mIsBeingDragged && getChildCount() > 0) { + mActivePointerId = INVALID_POINTER; + mIsBeingDragged = false; + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + break; + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; + } + return true; + } + + private void onSecondaryPointerUp(MotionEvent ev) { + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> + MotionEvent.ACTION_POINTER_ID_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mLastMotionX = ev.getX(newPointerIndex); + mLastMotionY = ev.getY(newPointerIndex); + mActivePointerId = ev.getPointerId(newPointerIndex); + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + } + } + + /** + *

+ * Finds the next focusable component that fits in the specified bounds. + *

+ * + * @param topFocus look for a candidate is the one at the top of the bounds + * if topFocus is true, or at the bottom of the bounds if topFocus is + * false + * @param top the top offset of the bounds in which a focusable must be + * found + * @param bottom the bottom offset of the bounds in which a focusable must + * be found + * @return the next focusable component in the bounds or null if none can + * be found + */ + private View findFocusableViewInBoundsV(boolean topFocus, int top, int bottom) { + + List focusables = getFocusables(View.FOCUS_FORWARD); + View focusCandidate = null; + + /* + * A fully contained focusable is one where its top is below the bound's + * top, and its bottom is above the bound's bottom. A partially + * contained focusable is one where some part of it is within the + * bounds, but it also has some part that is not within bounds. A fully contained + * focusable is preferred to a partially contained focusable. + */ + boolean foundFullyContainedFocusable = false; + + int count = focusables.size(); + for (int i = 0; i < count; i++) { + View view = focusables.get(i); + int viewTop = view.getTop(); + int viewBottom = view.getBottom(); + + if (top < viewBottom && viewTop < bottom) { + /* + * the focusable is in the target area, it is a candidate for + * focusing + */ + + final boolean viewIsFullyContained = (top < viewTop) && + (viewBottom < bottom); + + if (focusCandidate == null) { + /* No candidate, take this one */ + focusCandidate = view; + foundFullyContainedFocusable = viewIsFullyContained; + } else { + final boolean viewIsCloserToBoundary = + (topFocus && viewTop < focusCandidate.getTop()) || + (!topFocus && viewBottom > focusCandidate + .getBottom()); + + if (foundFullyContainedFocusable) { + if (viewIsFullyContained && viewIsCloserToBoundary) { + /* + * We're dealing with only fully contained views, so + * it has to be closer to the boundary to beat our + * candidate + */ + focusCandidate = view; + } + } else { + if (viewIsFullyContained) { + /* Any fully contained view beats a partially contained view */ + focusCandidate = view; + foundFullyContainedFocusable = true; + } else if (viewIsCloserToBoundary) { + /* + * Partially contained view beats another partially + * contained view if it's closer + */ + focusCandidate = view; + } + } + } + } + } + + return focusCandidate; + } + + private View findFocusableViewInBoundsH(boolean leftFocus, int left, int right) { + + List focusables = getFocusables(View.FOCUS_FORWARD); + View focusCandidate = null; + + /* + * A fully contained focusable is one where its left is below the bound's + * left, and its right is above the bound's right. A partially + * contained focusable is one where some part of it is within the + * bounds, but it also has some part that is not within bounds. A fully contained + * focusable is preferred to a partially contained focusable. + */ + boolean foundFullyContainedFocusable = false; + + int count = focusables.size(); + for (int i = 0; i < count; i++) { + View view = focusables.get(i); + int viewLeft = view.getLeft(); + int viewRight = view.getRight(); + + if (left < viewRight && viewLeft < right) { + /* + * the focusable is in the target area, it is a candidate for + * focusing + */ + + final boolean viewIsFullyContained = (left < viewLeft) && + (viewRight < right); + + if (focusCandidate == null) { + /* No candidate, take this one */ + focusCandidate = view; + foundFullyContainedFocusable = viewIsFullyContained; + } else { + final boolean viewIsCloserToBoundary = + (leftFocus && viewLeft < focusCandidate.getLeft()) || + (!leftFocus && viewRight > focusCandidate.getRight()); + + if (foundFullyContainedFocusable) { + if (viewIsFullyContained && viewIsCloserToBoundary) { + /* + * We're dealing with only fully contained views, so + * it has to be closer to the boundary to beat our + * candidate + */ + focusCandidate = view; + } + } else { + if (viewIsFullyContained) { + /* Any fully contained view beats a partially contained view */ + focusCandidate = view; + foundFullyContainedFocusable = true; + } else if (viewIsCloserToBoundary) { + /* + * Partially contained view beats another partially + * contained view if it's closer + */ + focusCandidate = view; + } + } + } + } + } + + return focusCandidate; + } + + /** + *

Handles scrolling in response to a "home/end" shortcut press. This + * method will scroll the view to the top or bottom and give the focus + * to the topmost/bottommost component in the new visible area. If no + * component is a good candidate for focus, this scrollview reclaims the + * focus.

+ * + * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} + * to go the top of the view or + * {@link android.view.View#FOCUS_DOWN} to go the bottom + * @return true if the key event is consumed by this method, false otherwise + */ + public boolean fullScrollV(int direction) { + boolean down = direction == View.FOCUS_DOWN; + int height = getHeight(); + + mTempRect.top = 0; + mTempRect.bottom = height; + + if (down) { + int count = getChildCount(); + if (count > 0) { + View view = getChildAt(count - 1); + mTempRect.bottom = view.getBottom(); + mTempRect.top = mTempRect.bottom - height; + } + } + + return scrollAndFocusV(direction, mTempRect.top, mTempRect.bottom); + } + + public boolean fullScrollH(int direction) { + boolean right = direction == View.FOCUS_RIGHT; + int width = getWidth(); + + mTempRect.left = 0; + mTempRect.right = width; + + if (right) { + int count = getChildCount(); + if (count > 0) { + View view = getChildAt(0); + mTempRect.right = view.getRight(); + mTempRect.left = mTempRect.right - width; + } + } + + return scrollAndFocusH(direction, mTempRect.left, mTempRect.right); + } + + /** + *

Scrolls the view to make the area defined by top and + * bottom visible. This method attempts to give the focus + * to a component visible in this area. If no component can be focused in + * the new visible area, the focus is reclaimed by this scrollview.

+ * + * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} + * to go upward + * {@link android.view.View#FOCUS_DOWN} to downward + * @param top the top offset of the new area to be made visible + * @param bottom the bottom offset of the new area to be made visible + * @return true if the key event is consumed by this method, false otherwise + */ + private boolean scrollAndFocusV(int direction, int top, int bottom) { + boolean handled = true; + + int height = getHeight(); + int containerTop = getScrollY(); + int containerBottom = containerTop + height; + boolean up = direction == View.FOCUS_UP; + + View newFocused = findFocusableViewInBoundsV(up, top, bottom); + if (newFocused == null) { + newFocused = this; + } + + if (top >= containerTop && bottom <= containerBottom) { + handled = false; + } else { + int delta = up ? (top - containerTop) : (bottom - containerBottom); + doScrollY(delta); + } + + if (newFocused != findFocus() && newFocused.requestFocus(direction)) { + mScrollViewMovedFocus = true; + mScrollViewMovedFocus = false; + } + + return handled; + } + + private boolean scrollAndFocusH(int direction, int left, int right) { + boolean handled = true; + + int width = getWidth(); + int containerLeft = getScrollX(); + int containerRight = containerLeft + width; + boolean goLeft = direction == View.FOCUS_LEFT; + + View newFocused = findFocusableViewInBoundsH(goLeft, left, right); + if (newFocused == null) { + newFocused = this; + } + + if (left >= containerLeft && right <= containerRight) { + handled = false; + } else { + int delta = goLeft ? (left - containerLeft) : (right - containerRight); + doScrollX(delta); + } + + if (newFocused != findFocus() && newFocused.requestFocus(direction)) { + mScrollViewMovedFocus = true; + mScrollViewMovedFocus = false; + } + + return handled; + } + + /** + * Handle scrolling in response to an up or down arrow click. + * + * @param direction The direction corresponding to the arrow key that was + * pressed + * @return True if we consumed the event, false otherwise + */ + public boolean arrowScrollV(int direction) { + + View currentFocused = findFocus(); + if (currentFocused == this) currentFocused = null; + + View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); + + final int maxJump = getMaxScrollAmountV(); + + if (nextFocused != null && isWithinDeltaOfScreenV(nextFocused, maxJump, getHeight())) { + nextFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(nextFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreenV(mTempRect); + doScrollY(scrollDelta); + nextFocused.requestFocus(direction); + } else { + // no new focus + int scrollDelta = maxJump; + + if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) { + scrollDelta = getScrollY(); + } else if (direction == View.FOCUS_DOWN) { + if (getChildCount() > 0) { + int daBottom = getChildAt(0).getBottom(); + + int screenBottom = getScrollY() + getHeight(); + + if (daBottom - screenBottom < maxJump) { + scrollDelta = daBottom - screenBottom; + } + } + } + if (scrollDelta == 0) { + return false; + } + doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta); + } + + if (currentFocused != null && currentFocused.isFocused() + && isOffScreenV(currentFocused)) { + // previously focused item still has focus and is off screen, give + // it up (take it back to ourselves) + // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are + // sure to + // get it) + final int descendantFocusability = getDescendantFocusability(); // save + setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); + requestFocus(); + setDescendantFocusability(descendantFocusability); // restore + } + return true; + } + + public boolean arrowScrollH(int direction) { + + View currentFocused = findFocus(); + if (currentFocused == this) currentFocused = null; + + View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); + + final int maxJump = getMaxScrollAmountH(); + + if (nextFocused != null && isWithinDeltaOfScreenH(nextFocused, maxJump)) { + nextFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(nextFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreenH(mTempRect); + doScrollX(scrollDelta); + nextFocused.requestFocus(direction); + } else { + // no new focus + int scrollDelta = maxJump; + + if (direction == View.FOCUS_LEFT && getScrollX() < scrollDelta) { + scrollDelta = getScrollX(); + } else if (direction == View.FOCUS_RIGHT && getChildCount() > 0) { + + int daRight = getChildAt(0).getRight(); + + int screenRight = getScrollX() + getWidth(); + + if (daRight - screenRight < maxJump) { + scrollDelta = daRight - screenRight; + } + } + if (scrollDelta == 0) { + return false; + } + doScrollX(direction == View.FOCUS_RIGHT ? scrollDelta : -scrollDelta); + } + + if (currentFocused != null && currentFocused.isFocused() + && isOffScreenH(currentFocused)) { + // previously focused item still has focus and is off screen, give + // it up (take it back to ourselves) + // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are + // sure to + // get it) + final int descendantFocusability = getDescendantFocusability(); // save + setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); + requestFocus(); + setDescendantFocusability(descendantFocusability); // restore + } + return true; + } + + /** + * @return whether the descendant of this scroll view is scrolled off + * screen. + */ + private boolean isOffScreenV(View descendant) { + return !isWithinDeltaOfScreenV(descendant, 0, getHeight()); + } + + private boolean isOffScreenH(View descendant) { + return !isWithinDeltaOfScreenH(descendant, 0); + } + + /** + * @return whether the descendant of this scroll view is within delta + * pixels of being on the screen. + */ + private boolean isWithinDeltaOfScreenV(View descendant, int delta, int height) { + descendant.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(descendant, mTempRect); + + return (mTempRect.bottom + delta) >= getScrollY() + && (mTempRect.top - delta) <= (getScrollY() + height); + } + + private boolean isWithinDeltaOfScreenH(View descendant, int delta) { + descendant.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(descendant, mTempRect); + + return (mTempRect.right + delta) >= getScrollX() + && (mTempRect.left - delta) <= (getScrollX() + getWidth()); + } + + /** + * Smooth scroll by a Y delta + * + * @param delta the number of pixels to scroll by on the Y axis + */ + private void doScrollY(int delta) { + if (delta != 0) { + if (mSmoothScrollingEnabled) { + smoothScrollBy(0, delta); + } else { + scrollBy(0, delta); + } + } + } + + private void doScrollX(int delta) { + if (delta != 0) { + if (mSmoothScrollingEnabled) { + smoothScrollBy(delta, 0); + } else { + scrollBy(delta, 0); + } + } + } + + /** + * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. + * + * @param dx the number of pixels to scroll by on the X axis + * @param dy the number of pixels to scroll by on the Y axis + */ + public void smoothScrollBy(int dx, int dy) { + if (getChildCount() == 0) { + // Nothing to do. + return; + } + long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; + if (duration > ANIMATED_SCROLL_GAP) { + final int height = getHeight() - getPaddingBottom() - getPaddingTop(); + final int bottom = getChildAt(0).getHeight(); + final int maxY = Math.max(0, bottom - height); + final int scrollY = getScrollY(); + dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY; + + final int width = getWidth() - getPaddingRight() - getPaddingLeft(); + final int right = getChildAt(0).getWidth(); + final int maxX = Math.max(0, right - width); + final int scrollX = getScrollX(); + dx = Math.max(0, Math.min(scrollX + dx, maxX)) - scrollX; + + mScroller.startScroll(scrollX, scrollY, dx, dy); + invalidate(); + } else { + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + scrollBy(dx, dy); + } + mLastScroll = AnimationUtils.currentAnimationTimeMillis(); + } + + /** + * Like {@link #scrollTo}, but scroll smoothly instead of immediately. + * + * @param x the position where to scroll on the X axis + * @param y the position where to scroll on the Y axis + */ + public final void smoothScrollTo(int x, int y) { + smoothScrollBy(x - getScrollX(), y - getScrollY()); + } + + /** + *

The scroll range of a scroll view is the overall height of all of its + * children.

+ */ + @Override + protected int computeVerticalScrollRange() { + final int count = getChildCount(); + final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop(); + if (count == 0) { + return contentHeight; + } + + return getChildAt(0).getBottom(); + } + + @Override + protected int computeHorizontalScrollRange() { + final int count = getChildCount(); + final int contentWidth = getWidth() - getPaddingLeft() - getPaddingRight(); + if (count == 0) { + return contentWidth; + } + + return getChildAt(0).getRight(); + } + + @Override + protected int computeVerticalScrollOffset() { + return Math.max(0, super.computeVerticalScrollOffset()); + } + + @Override + protected int computeHorizontalScrollOffset() { + return Math.max(0, super.computeHorizontalScrollOffset()); + } + + @Override + protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { + int childWidthMeasureSpec; + int childHeightMeasureSpec; + + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + @Override + protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED); + final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + @Override + public void computeScroll() { + if (mScroller.computeScrollOffset()) { + // This is called at drawing time by ViewGroup. We don't want to + // re-show the scrollbars at this point, which scrollTo will do, + // so we replicate most of scrollTo here. + // + // It's a little odd to call onScrollChanged from inside the drawing. + // + // It is, except when you remember that computeScroll() is used to + // animate scrolling. So unless we want to defer the onScrollChanged() + // until the end of the animated scrolling, we don't really have a + // choice here. + // + // I agree. The alternative, which I think would be worse, is to post + // something and tell the subclasses later. This is bad because there + // will be a window where mScrollX/Y is different from what the app + // thinks it is. + // + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + + if (getChildCount() > 0) { + View child = getChildAt(0); + x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()); + y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight()); + super.scrollTo(x, y); + } + awakenScrollBars(); + + // Keep on drawing until the animation has finished. + postInvalidate(); + } + } + + /** + * Scrolls the view to the given child. + * + * @param child the View to scroll to + */ + private void scrollToChild(View child) { + child.getDrawingRect(mTempRect); + + /* Offset from child's local coordinates to ScrollView coordinates */ + offsetDescendantRectToMyCoords(child, mTempRect); + + int scrollDeltaV = computeScrollDeltaToGetChildRectOnScreenV(mTempRect); + int scrollDeltaH = computeScrollDeltaToGetChildRectOnScreenH(mTempRect); + + if (scrollDeltaH != 0 || scrollDeltaV != 0) { + scrollBy(scrollDeltaH, scrollDeltaV); + } + } + + /** + * If rect is off screen, scroll just enough to get it (or at least the + * first screen size chunk of it) on screen. + * + * @param rect The rectangle. + * @param immediate True to scroll immediately without animation + * @return true if scrolling was performed + */ + private boolean scrollToChildRect(Rect rect, boolean immediate) { + final int deltaV = computeScrollDeltaToGetChildRectOnScreenV(rect); + final int deltaH = computeScrollDeltaToGetChildRectOnScreenH(rect); + final boolean scroll = deltaH != 0 || deltaV != 0; + if (scroll) { + if (immediate) { + scrollBy(deltaH, deltaV); + } else { + smoothScrollBy(deltaH, deltaV); + } + } + return scroll; + } + + /** + * Compute the amount to scroll in the Y direction in order to get + * a rectangle completely on the screen (or, if taller than the screen, + * at least the first screen size chunk of it). + * + * @param rect The rect. + * @return The scroll delta. + */ + protected int computeScrollDeltaToGetChildRectOnScreenV(Rect rect) { + if (getChildCount() == 0) return 0; + + int height = getHeight(); + int screenTop = getScrollY(); + int screenBottom = screenTop + height; + + int fadingEdge = getVerticalFadingEdgeLength(); + + // leave room for top fading edge as long as rect isn't at very top + if (rect.top > 0) { + screenTop += fadingEdge; + } + + // leave room for bottom fading edge as long as rect isn't at very bottom + if (rect.bottom < getChildAt(0).getHeight()) { + screenBottom -= fadingEdge; + } + + int scrollYDelta = 0; + + if (rect.bottom > screenBottom && rect.top > screenTop) { + // need to move down to get it in view: move down just enough so + // that the entire rectangle is in view (or at least the first + // screen size chunk). + + if (rect.height() > height) { + // just enough to get screen size chunk on + scrollYDelta += (rect.top - screenTop); + } else { + // get entire rect at bottom of screen + scrollYDelta += (rect.bottom - screenBottom); + } + + // make sure we aren't scrolling beyond the end of our content + int bottom = getChildAt(0).getBottom(); + int distanceToBottom = bottom - screenBottom; + scrollYDelta = Math.min(scrollYDelta, distanceToBottom); + + } else if (rect.top < screenTop && rect.bottom < screenBottom) { + // need to move up to get it in view: move up just enough so that + // entire rectangle is in view (or at least the first screen + // size chunk of it). + + if (rect.height() > height) { + // screen size chunk + scrollYDelta -= (screenBottom - rect.bottom); + } else { + // entire rect at top + scrollYDelta -= (screenTop - rect.top); + } + + // make sure we aren't scrolling any further than the top our content + scrollYDelta = Math.max(scrollYDelta, -getScrollY()); + } + return scrollYDelta; + } + + protected int computeScrollDeltaToGetChildRectOnScreenH(Rect rect) { + if (getChildCount() == 0) return 0; + + int width = getWidth(); + int screenLeft = getScrollX(); + int screenRight = screenLeft + width; + + int fadingEdge = getHorizontalFadingEdgeLength(); + + // leave room for left fading edge as long as rect isn't at very left + if (rect.left > 0) { + screenLeft += fadingEdge; + } + + // leave room for right fading edge as long as rect isn't at very right + if (rect.right < getChildAt(0).getWidth()) { + screenRight -= fadingEdge; + } + + int scrollXDelta = 0; + + if (rect.right > screenRight && rect.left > screenLeft) { + // need to move right to get it in view: move right just enough so + // that the entire rectangle is in view (or at least the first + // screen size chunk). + + if (rect.width() > width) { + // just enough to get screen size chunk on + scrollXDelta += (rect.left - screenLeft); + } else { + // get entire rect at right of screen + scrollXDelta += (rect.right - screenRight); + } + + // make sure we aren't scrolling beyond the end of our content + int right = getChildAt(0).getRight(); + int distanceToRight = right - screenRight; + scrollXDelta = Math.min(scrollXDelta, distanceToRight); + + } else if (rect.left < screenLeft && rect.right < screenRight) { + // need to move right to get it in view: move right just enough so that + // entire rectangle is in view (or at least the first screen + // size chunk of it). + + if (rect.width() > width) { + // screen size chunk + scrollXDelta -= (screenRight - rect.right); + } else { + // entire rect at left + scrollXDelta -= (screenLeft - rect.left); + } + + // make sure we aren't scrolling any further than the left our content + scrollXDelta = Math.max(scrollXDelta, -getScrollX()); + } + return scrollXDelta; + } + + @Override + public void requestChildFocus(View child, View focused) { + if (!mScrollViewMovedFocus) { + if (!mIsLayoutDirty) { + scrollToChild(focused); + } else { + // The child may not be laid out yet, we can't compute the scroll yet + mChildToScrollTo = focused; + } + } + super.requestChildFocus(child, focused); + } + + + /** + * When looking for focus in children of a scroll view, need to be a little + * more careful not to give focus to something that is scrolled off screen. + *

+ * This is more expensive than the default {@link android.view.ViewGroup} + * implementation, otherwise this behavior might have been made the default. + */ + @Override + protected boolean onRequestFocusInDescendants(int direction, + Rect previouslyFocusedRect) { + + // convert from forward / backward notation to up / down / left / right + // (ugh). + // TODO: FUCK + // if (direction == View.FOCUS_FORWARD) { + // direction = View.FOCUS_RIGHT; + // } else if (direction == View.FOCUS_BACKWARD) { + // direction = View.FOCUS_LEFT; + // } + + final View nextFocus = previouslyFocusedRect == null ? + FocusFinder.getInstance().findNextFocus(this, null, direction) : + FocusFinder.getInstance().findNextFocusFromRect(this, + previouslyFocusedRect, direction); + + if (nextFocus == null) { + return false; + } + + // if (isOffScreenH(nextFocus)) { + // return false; + // } + + return nextFocus.requestFocus(direction, previouslyFocusedRect); + } + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rectangle, + boolean immediate) { + // offset into coordinate space of this scroll view + rectangle.offset(child.getLeft() - child.getScrollX(), + child.getTop() - child.getScrollY()); + + return scrollToChildRect(rectangle, immediate); + } + + @Override + public void requestLayout() { + mIsLayoutDirty = true; + super.requestLayout(); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + mIsLayoutDirty = false; + // Give a child focus if it needs it + if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { + scrollToChild(mChildToScrollTo); + } + mChildToScrollTo = null; + + // Calling this with the present values causes it to re-clam them + scrollTo(getScrollX(), getScrollY()); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + View currentFocused = findFocus(); + if (null == currentFocused || this == currentFocused) + return; + + // If the currently-focused view was visible on the screen when the + // screen was at the old height, then scroll the screen to make that + // view visible with the new screen height. + if (isWithinDeltaOfScreenV(currentFocused, 0, oldh)) { + currentFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(currentFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreenV(mTempRect); + doScrollY(scrollDelta); + } + + final int maxJump = getRight() - getLeft(); + if (isWithinDeltaOfScreenH(currentFocused, maxJump)) { + currentFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(currentFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreenH(mTempRect); + doScrollX(scrollDelta); + } + } + + /** + * Return true if child is an descendant of parent, (or equal to the parent). + */ + private boolean isViewDescendantOf(View child, View parent) { + if (child == parent) { + return true; + } + + final ViewParent theParent = child.getParent(); + return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); + } + + /** + * Fling the scroll view + * + * @param velocityY The initial velocity in the Y direction. Positive + * numbers mean that the finger/cursor is moving down the screen, + * which means we want to scroll towards the top. + */ + public void fling(int velocityX, int velocityY) { + if (getChildCount() > 0) { + int width = getWidth() - getPaddingRight() - getPaddingLeft(); + int right = getChildAt(0).getWidth(); + + int height = getHeight() - getPaddingBottom() - getPaddingTop(); + int bottom = getChildAt(0).getHeight(); + + mScroller.fling(getScrollX(), getScrollY(), velocityX, velocityY, + 0, Math.max(0, right - width), + 0, Math.max(0, bottom - height)); + + // final boolean movingDown = velocityX > 0 || velocityY > 0; + // + // View newFocused = + // findFocusableViewInMyBoundsV(movingDown, mScroller.getFinalY(), findFocus()); + // if (newFocused == null) { + // newFocused = this; + // } + // + // if (newFocused != findFocus() + // && newFocused.requestFocus(movingDown ? View.FOCUS_DOWN : View.FOCUS_UP)) { + // mScrollViewMovedFocus = true; + // mScrollViewMovedFocus = false; + // } + + invalidate(); + } + } + + /** + * {@inheritDoc} + *

+ *

This version also clamps the scrolling to the bounds of our child. + */ + @Override + public void scrollTo(int x, int y) { + // we rely on the fact the View.scrollBy calls scrollTo. + if (getChildCount() > 0) { + View child = getChildAt(0); + x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()); + y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight()); + if (x != getScrollX() || y != getScrollY()) { + super.scrollTo(x, y); + } + } + } + + private int clamp(int n, int my, int child) { + if (my >= child || n < 0) { + /* my >= child is this case: + * |--------------- me ---------------| + * |------ child ------| + * or + * |--------------- me ---------------| + * |------ child ------| + * or + * |--------------- me ---------------| + * |------ child ------| + * + * n < 0 is this case: + * |------ me ------| + * |-------- child --------| + * |-- mScrollX --| + */ + return 0; + } + if ((my + n) > child) { + /* this case: + * |------ me ------| + * |------ child ------| + * |-- mScrollX --| + */ + return child - my; + } + return n; + } + + public boolean isFlingEnabled() { + return mFlingEnabled; + } + + public void setFlingEnabled(boolean flingEnabled) { + this.mFlingEnabled = flingEnabled; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/JavaScriptHighlighter.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/JavaScriptHighlighter.java new file mode 100644 index 00000000..db3f9d59 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/JavaScriptHighlighter.java @@ -0,0 +1,110 @@ +package com.stardust.scriptdroid.ui.edit.editor; + + +import android.text.Editable; +import android.util.Log; + +import com.stardust.autojs.rhino.TokenStream; +import com.stardust.pio.UncheckedIOException; +import com.stardust.scriptdroid.ui.edit.theme.Theme; +import com.stardust.scriptdroid.ui.widget.SimpleTextWatcher; + +import org.mozilla.javascript.Token; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChangedListener { + + + public static class HighlightTokens { + + private int[] mColors; + private String mText; + + public HighlightTokens(String text) { + mColors = new int[text.length()]; + mText = text; + } + + + public int getCharColor(int i) { + return mColors[i]; + } + + + public void addToken(int tokenStart, int tokenEnd, int color) { + for (int i = tokenStart; i < tokenEnd; i++) { + mColors[i] = color; + } + } + + @Override + public String toString() { + return "HighlightTokens{" + + "mColors=" + Arrays.toString(mColors) + + '}'; + } + + public int getCharCount() { + return mColors.length; + } + + public String getText() { + return mText; + } + } + + private Theme mTheme; + private CodeEditText mCodeEditText; + private ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); + private AtomicInteger mRunningHighlighterId = new AtomicInteger(); + + public JavaScriptHighlighter(Theme theme, CodeEditText codeEditText) { + mTheme = theme; + mCodeEditText = codeEditText; + codeEditText.addTextChangedListener(new SimpleTextWatcher(this)); + } + + @Override + public void afterTextChanged(Editable s) { + updateTokens(s.toString()); + } + + + public void setTheme(Theme theme) { + mTheme = theme; + } + + private void updateTokens(String sourceString) { + final int id = mRunningHighlighterId.incrementAndGet(); + mExecutorService.execute(() -> { + try { + updateTokens(sourceString, id); + } catch (IOException neverHappen) { + throw new UncheckedIOException(neverHappen); + } + + }); + } + + private void updateTokens(String sourceString, int id) throws IOException { + TokenStream ts = new TokenStream(null, sourceString, 0); + HighlightTokens highlightTokens = new HighlightTokens(sourceString); + int token; + while ((token = ts.getToken()) != Token.EOF) { + if (mRunningHighlighterId.get() != id) + return; + int color = mTheme.getColorForToken(token); + highlightTokens.addToken(ts.getTokenBeg(), ts.getTokenEnd(), color); + } + mCodeEditText.updateHighlightTokens(highlightTokens); + Log.d("Highlighter", "code = " + sourceString); + Log.d("Highlighter", "tokens = " + highlightTokens); + } + + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/LayoutHelper.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/LayoutHelper.java new file mode 100644 index 00000000..8c4d3f76 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/LayoutHelper.java @@ -0,0 +1,54 @@ +package com.stardust.scriptdroid.ui.edit.editor; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.text.Layout; +import android.text.TextUtils; + +/** + * Created by Stardust on 2018/2/13. + */ + +public class LayoutHelper { + + + private static final Rect sTempRect = new Rect(); + + public static long getLineRangeForDraw(Layout layout, Canvas canvas) { + int dtop, dbottom; + synchronized (sTempRect) { + if (!canvas.getClipBounds(sTempRect)) { + // Negative range end used as a special flag + return packRangeInLong(0, -1); + } + + dtop = sTempRect.top; + dbottom = sTempRect.bottom; + } + + final int top = Math.max(dtop, 0); + final int bottom = Math.min(layout.getLineTop(layout.getLineCount()), dbottom); + + if (top >= bottom) return packRangeInLong(0, -1); + return packRangeInLong(layout.getLineForVertical(top), layout.getLineForVertical(bottom)); + } + + /** + * Pack 2 int values into a long, useful as a return value for a range + */ + public static long packRangeInLong(int start, int end) { + return (((long) start) << 32) | end; + } + + /** + * Get the start value from a range packed in a long by {@link #packRangeInLong(int, int)} + */ + public static int unpackRangeStartFromLong(long range) { + return (int) (range >>> 32); + } + + public static int unpackRangeEndFromLong(long range) { + return (int) (range & 0x00000000FFFFFFFFL); + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/PreformEdit.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/PreformEdit.java new file mode 100644 index 00000000..cf4ca082 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor/PreformEdit.java @@ -0,0 +1,267 @@ +package com.stardust.scriptdroid.ui.edit.editor; +/* + * Copyright 2016. SHENQINCI(沈钦赐)<946736079@qq.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import android.support.annotation.NonNull; +import android.text.Editable; +import android.text.TextWatcher; +import android.widget.EditText; + +import java.util.Stack; + +/** + * 撤销和恢复撤销 + * Created by 沈钦赐 on 16/6/23. + */ +public class PreformEdit { + //操作序号(一次编辑可能对应多个操作,如替换文字,就是删除+插入) + int index; + //撤销栈 + Stack history = new Stack<>(); + //恢复栈 + Stack historyBack = new Stack<>(); + + private Editable editable; + private EditText editText; + //自动操作标志,防止重复回调,导致无限撤销 + private boolean flag = false; + + public PreformEdit(@NonNull EditText editText) { + CheckNull(editText, "EditText不能为空"); + this.editable = editText.getText(); + this.editText = editText; + editText.addTextChangedListener(new Watcher()); + } + + protected void onEditableChanged(Editable s) { + + } + + protected void onTextChanged(Editable s) { + + } + + + /** + * 清理记录 + * Clear history. + */ + public final void clearHistory() { + history.clear(); + historyBack.clear(); + } + + public boolean canUndo() { + return !history.empty(); + } + + /** + * 撤销 + * Undo. + */ + public final void undo() { + if (history.empty()) return; + //锁定操作 + flag = true; + Action action = history.pop(); + historyBack.push(action); + if (action.isAdd) { + //撤销添加 + editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); + editText.setSelection(action.startCursor, action.startCursor); + } else { + //插销删除 + editable.insert(action.startCursor, action.actionTarget); + if (action.endCursor == action.startCursor) { + editText.setSelection(action.startCursor + action.actionTarget.length()); + } else { + editText.setSelection(action.startCursor, action.endCursor); + } + } + //释放操作 + flag = false; + //判断是否是下一个动作是否和本动作是同一个操作,直到不同为止 + if (!history.empty() && history.peek().index == action.index) { + undo(); + } + } + + public boolean canRedo() { + return !historyBack.empty(); + } + + /** + * 恢复 + * Redo. + */ + public final void redo() { + if (historyBack.empty()) return; + flag = true; + Action action = historyBack.pop(); + history.push(action); + if (action.isAdd) { + //恢复添加 + editable.insert(action.startCursor, action.actionTarget); + if (action.endCursor == action.startCursor) { + editText.setSelection(action.startCursor + action.actionTarget.length()); + } else { + editText.setSelection(action.startCursor, action.endCursor); + } + } else { + //恢复删除 + editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); + editText.setSelection(action.startCursor, action.startCursor); + } + flag = false; + //判断是否是下一个动作是否和本动作是同一个操作 + if (!historyBack.empty() && historyBack.peek().index == action.index) + redo(); + } + + /** + * 首次设置文本 + * Set default text. + */ + public final void setDefaultText(CharSequence text) { + clearHistory(); + flag = true; + editable.replace(0, editable.length(), text); + flag = false; + } + + public boolean isTextChanged() { + return !history.empty(); + } + + private class Watcher implements TextWatcher { + + /** + * Before text changed. + * + * @param s the s + * @param start the start 起始光标 + * @param count the endCursor 选择数量 + * @param after the after 替换增加的文字数 + */ + @Override + public final void beforeTextChanged(CharSequence s, int start, int count, int after) { + if (flag) return; + int end = start + count; + if (end > start && end <= s.length()) { + CharSequence charSequence = s.subSequence(start, end); + //删除了文字 + if (charSequence.length() > 0) { + Action action = new Action(charSequence, start, false); + if (count > 1) { + //如果一次超过一个字符,说名用户选择了,然后替换或者删除操作 + action.setSelectCount(count); + } else if (count == 1 && count == after) { + //一个字符替换 + action.setSelectCount(count); + } + //还有一种情况:选择一个字符,然后删除(暂时没有考虑这种情况) + + history.push(action); + historyBack.clear(); + action.setIndex(++index); + } + } + } + + /** + * On text changed. + * + * @param s the s + * @param start the start 起始光标 + * @param before the before 选择数量 + * @param count the endCursor 添加的数量 + */ + @Override + public final void onTextChanged(CharSequence s, int start, int before, int count) { + if (flag) return; + int end = start + count; + if (end > start) { + CharSequence charSequence = s.subSequence(start, end); + //添加文字 + if (charSequence.length() > 0) { + Action action = new Action(charSequence, start, true); + + history.push(action); + historyBack.clear(); + if (before > 0) { + //文字替换(先删除再增加),删除和增加是同一个操作,所以不需要增加序号 + action.setIndex(index); + } else { + action.setIndex(++index); + } + } + } + } + + @Override + public final void afterTextChanged(Editable s) { + if (flag) return; + if (s != editable) { + editable = s; + onEditableChanged(s); + } + PreformEdit.this.onTextChanged(s); + } + + } + + private class Action { + /** + * 改变字符. + */ + CharSequence actionTarget; + /** + * 光标位置. + */ + int startCursor; + int endCursor; + /** + * 标志增加操作. + */ + boolean isAdd; + /** + * 操作序号. + */ + int index; + + + public Action(CharSequence actionTag, int startCursor, boolean add) { + this.actionTarget = actionTag; + this.startCursor = startCursor; + this.endCursor = startCursor; + this.isAdd = add; + } + + public void setSelectCount(int count) { + this.endCursor = endCursor + count; + } + + public void setIndex(int index) { + this.index = index; + } + } + + + private static void CheckNull(Object o, String message) { + if (o == null) throw new IllegalStateException(message); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/theme/Theme.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/theme/Theme.java new file mode 100644 index 00000000..5e582ff1 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/theme/Theme.java @@ -0,0 +1,114 @@ +package com.stardust.scriptdroid.ui.edit.theme; + +import android.graphics.Color; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import com.google.gson.Gson; +import com.stardust.scriptdroid.model.editor.EditorTheme; +import com.stardust.scriptdroid.model.editor.TokenColor; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Token; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Map; + +import static org.mozilla.javascript.Token.*; + +/** + * Created by Stardust on 2018/2/16. + */ + +public class Theme { + + + private int mBackgroundColor; + private int mForegroundColor; + private int mLineNumberColor; + private SparseIntArray mTokenColors = new SparseIntArray(); + private int mImeBarBackgroundColor; + private int mImeBarForegroundColor; + + public Theme(EditorTheme theme) { + mBackgroundColor = Color.parseColor(theme.getEditorColors().getEditorBackground()); + mForegroundColor = Color.parseColor(theme.getEditorColors().getEditorForeground()); + mLineNumberColor = Color.parseColor(theme.getEditorColors().getEditorIndentGuideBackground()); + mImeBarBackgroundColor = Color.parseColor(theme.getEditorColors().getImeBackgroundColor()); + mImeBarForegroundColor = Color.parseColor(theme.getEditorColors().getImeForegroundColor()); + + for (TokenColor tokenColor : theme.getTokenColors()) { + String foregroundStr = tokenColor.getSettings().getForeground(); + if (foregroundStr == null) + continue; + int foreground = Color.parseColor(foregroundStr); + for (String scope : tokenColor.getScope()) { + setTokenColor(scope, foreground); + } + } + } + + private void setTokenColor(String scope, int foreground) { + for (int token : TokenMapping.getTokensForScope(scope)) { + mTokenColors.put(token, foreground); + } + } + + public int getColorForToken(int token) { + return mTokenColors.get(token, mForegroundColor); + } + + public int getLineNumberColor() { + return mLineNumberColor; + } + + public static Theme getDefault(android.content.Context context) { + return fromAssetsJson(context, "editor/theme/dark_plus.json"); + } + + public static Theme fromJson(String json) { + EditorTheme theme = EditorTheme.fromJson(json); + if (theme == null) + return null; + return new Theme(theme); + } + + + public static Theme fromJson(Reader reader) { + EditorTheme theme = EditorTheme.fromJson(reader); + if (theme == null) + return null; + return new Theme(theme); + } + + public static Theme fromAssetsJson(android.content.Context context, String assetsPath) { + try { + return fromJson(new InputStreamReader(context.getAssets().open(assetsPath))); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + public int getBackgroundColor() { + return mBackgroundColor; + } + + public int getImeBarBackgroundColor() { + return mImeBarBackgroundColor; + } + + public void setImeBarBackgroundColor(int imeBarBackgroundColor) { + mImeBarBackgroundColor = imeBarBackgroundColor; + } + + public int getImeBarForegroundColor() { + return mImeBarForegroundColor; + } + + public void setImeBarForegroundColor(int imeBarForegroundColor) { + mImeBarForegroundColor = imeBarForegroundColor; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/theme/TokenMapping.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/theme/TokenMapping.java new file mode 100644 index 00000000..a8cb4e08 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/theme/TokenMapping.java @@ -0,0 +1,68 @@ +package com.stardust.scriptdroid.ui.edit.theme; + + +import org.mozilla.javascript.Token; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Created by Stardust on 2018/2/21. + */ + +public class TokenMapping { + + + private static final List KEYWORD = tokenNamesToTypes(Arrays.asList("return", "new", "delete", "typeof", "null", "this", "false", "true", "throw", "in", "instanceof", "yield", "try", "function", "if", "else", "switch", "case", "default", "while", "do", "for", "break", "continue", "var", "with", "catch", "finally", "void", "let", "const", "debugger")); + private static final List KEYWORD_CONTROL = tokenNamesToTypes(Arrays.asList("if", "else", "switch", "case", "break", "continue", "goto", "return", "try", "catch", "throw", "finally")); + private static final List KEYWORD_OPERATOR = Collections.emptyList(); + + public static List getTokensForScope(String scope) { + switch (scope) { + case "keyword": + return KEYWORD; + case "keyword.operator": + return KEYWORD_OPERATOR; + case "keyword.control": + return KEYWORD_CONTROL; + default: + int token = tokenNameToType(scope); + if (Token.isValidToken(token)) { + return Collections.singletonList(token); + } + } + return Collections.emptyList(); + } + + public static int tokenNameToType(String name) { + switch (name) { + case "this.self": + return Token.THIS; + case "keyword.operator.quantifier.regexp": + return Token.REGEXP; + case "variable": + return Token.NAME; + case "constant.numeric": + return Token.NUMBER; + + } + for (int token = Token.ERROR; token < Token.LAST_TOKEN; token++) { + if (token == Token.STRICT_SETNAME || token == Token.SETCONSTVAR) + continue; + if (Token.typeToName(token).equalsIgnoreCase(name)) { + return token; + } + } + return -2; + } + + private static List tokenNamesToTypes(List tokenNames) { + List types = new ArrayList<>(tokenNames.size()); + for (String name : tokenNames) { + types.add(tokenNameToType(name)); + } + return types; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/widget/SimpleTextWatcher.java b/app/src/main/java/com/stardust/scriptdroid/ui/widget/SimpleTextWatcher.java new file mode 100644 index 00000000..f648d81d --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/widget/SimpleTextWatcher.java @@ -0,0 +1,66 @@ +package com.stardust.scriptdroid.ui.widget; + +import android.text.Editable; +import android.text.TextWatcher; + +/** + * Created by Stardust on 2018/2/16. + */ + +public class SimpleTextWatcher implements TextWatcher { + + public interface AfterTextChangedListener { + void afterTextChanged(Editable s); + } + + public interface BeforeTextChangedListener { + void beforeTextChanged(CharSequence s, int start, int count, int after); + } + + public interface OnTextChangedListener { + void onTextChanged(CharSequence s, int start, int before, int count); + } + + private BeforeTextChangedListener mBeforeTextChangedListener; + private OnTextChangedListener mOnTextChangedListener; + private AfterTextChangedListener mAfterTextChangedListener; + + public SimpleTextWatcher(BeforeTextChangedListener beforeTextChangedListener, OnTextChangedListener onTextChangedListener, AfterTextChangedListener afterTextChangedListener) { + mBeforeTextChangedListener = beforeTextChangedListener; + mOnTextChangedListener = onTextChangedListener; + mAfterTextChangedListener = afterTextChangedListener; + } + + public SimpleTextWatcher(AfterTextChangedListener afterTextChangedListener) { + mAfterTextChangedListener = afterTextChangedListener; + } + + public SimpleTextWatcher(BeforeTextChangedListener beforeTextChangedListener) { + mBeforeTextChangedListener = beforeTextChangedListener; + } + + public SimpleTextWatcher(OnTextChangedListener onTextChangedListener) { + mOnTextChangedListener = onTextChangedListener; + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + if (mBeforeTextChangedListener != null) { + mBeforeTextChangedListener.beforeTextChanged(s, start, count, after); + } + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (mOnTextChangedListener != null) { + mOnTextChangedListener.onTextChanged(s, start, before, count); + } + } + + @Override + public void afterTextChanged(Editable s) { + if (mAfterTextChangedListener != null) { + mAfterTextChangedListener.afterTextChanged(s); + } + } +} diff --git a/app/src/main/res/drawable/code_edit_text_cursor.xml b/app/src/main/res/drawable/code_edit_text_cursor.xml new file mode 100644 index 00000000..be6967b7 --- /dev/null +++ b/app/src/main/res/drawable/code_edit_text_cursor.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/code_editor.xml b/app/src/main/res/layout/code_editor.xml new file mode 100644 index 00000000..89c466fe --- /dev/null +++ b/app/src/main/res/layout/code_editor.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/editor_view.xml b/app/src/main/res/layout/editor_view.xml index 3f0aa445..755e0dac 100644 --- a/app/src/main/res/layout/editor_view.xml +++ b/app/src/main/res/layout/editor_view.xml @@ -119,7 +119,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - 10) { + lowerCaseBound = (char) ('a' + radix - 10); + upperCaseBound = (char) ('A' + radix - 10); + } + int end; + double sum = 0.0; + for (end = start; end < len; end++) { + char c = s.charAt(end); + int newDigit; + if ('0' <= c && c <= digitMax) + newDigit = c - '0'; + else if ('a' <= c && c < lowerCaseBound) + newDigit = c - 'a' + 10; + else if ('A' <= c && c < upperCaseBound) + newDigit = c - 'A' + 10; + else + break; + sum = sum * radix + newDigit; + } + if (start == end) { + return NaN; + } + if (sum >= 9007199254740992.0) { + if (radix == 10) { + /* + * If we're accumulating a decimal number and the number is >= + * 2^53, then the result from the repeated multiply-add above + * may be inaccurate. Call Java to get the correct answer. + */ + try { + return Double.parseDouble(s.substring(start, end)); + } catch (NumberFormatException nfe) { + return NaN; + } + } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 + || radix == 32) { + /* + * The number may also be inaccurate for one of these bases. + * This happens if the addition in value*radix + digit causes a + * round-down to an even least significant mantissa bit when the + * first dropped bit is a one. If any of the following digits in + * the number (which haven't been added in yet) are nonzero then + * the correct action would have been to round up instead of + * down. An example of this occurs when reading the number + * 0x1000000000000081, which rounds to 0x1000000000000000 + * instead of 0x1000000000000100. + */ + int bitShiftInChar = 1; + int digit = 0; + + final int SKIP_LEADING_ZEROS = 0; + final int FIRST_EXACT_53_BITS = 1; + final int AFTER_BIT_53 = 2; + final int ZEROS_AFTER_54 = 3; + final int MIXED_AFTER_54 = 4; + + int state = SKIP_LEADING_ZEROS; + int exactBitsLimit = 53; + double factor = 0.0; + boolean bit53 = false; + // bit54 is the 54th bit (the first dropped from the mantissa) + boolean bit54 = false; + + for (;;) { + if (bitShiftInChar == 1) { + if (start == end) + break; + digit = s.charAt(start++); + if ('0' <= digit && digit <= '9') + digit -= '0'; + else if ('a' <= digit && digit <= 'z') + digit -= 'a' - 10; + else + digit -= 'A' - 10; + bitShiftInChar = radix; + } + bitShiftInChar >>= 1; + boolean bit = (digit & bitShiftInChar) != 0; + + switch (state) { + case SKIP_LEADING_ZEROS: + if (bit) { + --exactBitsLimit; + sum = 1.0; + state = FIRST_EXACT_53_BITS; + } + break; + case FIRST_EXACT_53_BITS: + sum *= 2.0; + if (bit) + sum += 1.0; + --exactBitsLimit; + if (exactBitsLimit == 0) { + bit53 = bit; + state = AFTER_BIT_53; + } + break; + case AFTER_BIT_53: + bit54 = bit; + factor = 2.0; + state = ZEROS_AFTER_54; + break; + case ZEROS_AFTER_54: + if (bit) { + state = MIXED_AFTER_54; + } + // fallthrough + case MIXED_AFTER_54: + factor *= 2; + break; + } + } + switch (state) { + case SKIP_LEADING_ZEROS: + sum = 0.0; + break; + case FIRST_EXACT_53_BITS: + case AFTER_BIT_53: + // do nothing + break; + case ZEROS_AFTER_54: + // x1.1 -> x1 + 1 (round up) + // x0.1 -> x0 (round down) + if (bit54 & bit53) + sum += 1.0; + sum *= factor; + break; + case MIXED_AFTER_54: + // x.100...1.. -> x + 1 (round up) + // x.0anything -> x (round down) + if (bit54) + sum += 1.0; + sum *= factor; + break; + } + } + /* We don't worry about inaccurate numbers for any other base. */ + } + return sum; + } + + /* + * For chars - because we need something out-of-range to check. (And + * checking EOF by exception is annoying.) Note distinction from EOF token + * type! + */ + private final static int EOF_CHAR = -1; + + private final static char BYTE_ORDER_MARK = '\uFEFF'; + + public TokenStream(Reader sourceReader, String sourceString, int lineno) { + this.lineno = lineno; + if (sourceReader != null) { + if (sourceString != null) + org.mozilla.javascript.Kit.codeBug(); + this.sourceReader = sourceReader; + this.sourceBuffer = new char[512]; + this.sourceEnd = 0; + } else { + if (sourceString == null) + org.mozilla.javascript.Kit.codeBug(); + this.sourceString = sourceString; + this.sourceEnd = sourceString.length(); + } + this.sourceCursor = this.cursor = 0; + } + + /* + * This function uses the cached op, string and number fields in + * TokenStream; if getToken has been called since the passed token was + * scanned, the op or string printed may be incorrect. + */ + String tokenToString(int token) { + if (Token.printTrees) { + String name = Token.name(token); + + switch (token) { + case Token.STRING: + case Token.REGEXP: + case Token.NAME: + return name + " `" + this.string + "'"; + + case Token.NUMBER: + return "NUMBER " + this.number; + } + + return name; + } + return ""; + } + + static boolean isKeyword(String s) { + return Token.EOF != stringToKeyword(s); + } + + private static int stringToKeyword(String name) { + return stringToKeywordForJS(name); + } + + /** + * JavaScript 1.8 and earlier + */ + private static int stringToKeywordForJS(String name) { + // #string_id_map# + // The following assumes that Token.EOF == 0 + final int Id_break = Token.BREAK, Id_case = Token.CASE, Id_continue = Token.CONTINUE, Id_default = Token.DEFAULT, Id_delete = Token.DELPROP, Id_do = Token.DO, Id_else = Token.ELSE, Id_export = Token.RESERVED, Id_false = Token.FALSE, Id_for = Token.FOR, Id_function = Token.FUNCTION, Id_if = Token.IF, Id_in = Token.IN, Id_let = Token.LET, // reserved + // ES5 + // strict + Id_new = Token.NEW, Id_null = Token.NULL, Id_return = Token.RETURN, Id_switch = Token.SWITCH, Id_this = Token.THIS, Id_true = Token.TRUE, Id_typeof = Token.TYPEOF, Id_var = Token.VAR, Id_void = Token.VOID, Id_while = Token.WHILE, Id_with = Token.WITH, Id_yield = Token.YIELD, // reserved + // ES5 + // strict + + // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c + Id_abstract = Token.RESERVED, // ES3 only + Id_boolean = Token.RESERVED, // ES3 only + Id_byte = Token.RESERVED, // ES3 only + Id_catch = Token.CATCH, Id_char = Token.RESERVED, // ES3 only + Id_class = Token.RESERVED, Id_const = Token.CONST, // reserved + Id_debugger = Token.DEBUGGER, Id_double = Token.RESERVED, // ES3 only + Id_enum = Token.RESERVED, Id_extends = Token.RESERVED, Id_final = Token.RESERVED, // ES3 + // only + Id_finally = Token.FINALLY, Id_float = Token.RESERVED, // ES3 only + Id_goto = Token.RESERVED, // ES3 only + Id_implements = Token.RESERVED, // ES3, ES5 strict + Id_import = Token.RESERVED, Id_instanceof = Token.INSTANCEOF, Id_int = Token.RESERVED, // ES3 + Id_interface = Token.RESERVED, // ES3, ES5 strict + Id_long = Token.RESERVED, // ES3 only + Id_native = Token.RESERVED, // ES3 only + Id_package = Token.RESERVED, // ES3, ES5 strict + Id_private = Token.RESERVED, // ES3, ES5 strict + Id_protected = Token.RESERVED, // ES3, ES5 strict + Id_public = Token.RESERVED, // ES3, ES5 strict + Id_short = Token.RESERVED, // ES3 only + Id_static = Token.RESERVED, // ES3, ES5 strict + Id_super = Token.RESERVED, Id_synchronized = Token.RESERVED, // ES3 only + Id_throw = Token.THROW, Id_throws = Token.RESERVED, // ES3 only + Id_transient = Token.RESERVED, // ES3 only + Id_try = Token.TRY, Id_volatile = Token.RESERVED; // ES3 only + + int id; + String s = name; + // #generated# Last update: 2007-04-18 13:53:30 PDT + L0: { + id = 0; + String X = null; + int c; + L: switch (s.length()) { + case 2: + c = s.charAt(1); + if (c == 'f') { + if (s.charAt(0) == 'i') { + id = Id_if; + break L0; + } + } else if (c == 'n') { + if (s.charAt(0) == 'i') { + id = Id_in; + break L0; + } + } else if (c == 'o') { + if (s.charAt(0) == 'd') { + id = Id_do; + break L0; + } + } + break L; + case 3: + switch (s.charAt(0)) { + case 'f': + if (s.charAt(2) == 'r' && s.charAt(1) == 'o') { + id = Id_for; + break L0; + } + break L; + case 'i': + if (s.charAt(2) == 't' && s.charAt(1) == 'n') { + id = Id_int; + break L0; + } + break L; + case 'l': + if (s.charAt(2) == 't' && s.charAt(1) == 'e') { + id = Id_let; + break L0; + } + break L; + case 'n': + if (s.charAt(2) == 'w' && s.charAt(1) == 'e') { + id = Id_new; + break L0; + } + break L; + case 't': + if (s.charAt(2) == 'y' && s.charAt(1) == 'r') { + id = Id_try; + break L0; + } + break L; + case 'v': + if (s.charAt(2) == 'r' && s.charAt(1) == 'a') { + id = Id_var; + break L0; + } + break L; + } + break L; + case 4: + switch (s.charAt(0)) { + case 'b': + X = "byte"; + id = Id_byte; + break L; + case 'c': + c = s.charAt(3); + if (c == 'e') { + if (s.charAt(2) == 's' && s.charAt(1) == 'a') { + id = Id_case; + break L0; + } + } else if (c == 'r') { + if (s.charAt(2) == 'a' && s.charAt(1) == 'h') { + id = Id_char; + break L0; + } + } + break L; + case 'e': + c = s.charAt(3); + if (c == 'e') { + if (s.charAt(2) == 's' && s.charAt(1) == 'l') { + id = Id_else; + break L0; + } + } else if (c == 'm') { + if (s.charAt(2) == 'u' && s.charAt(1) == 'n') { + id = Id_enum; + break L0; + } + } + break L; + case 'g': + X = "goto"; + id = Id_goto; + break L; + case 'l': + X = "long"; + id = Id_long; + break L; + case 'n': + X = "null"; + id = Id_null; + break L; + case 't': + c = s.charAt(3); + if (c == 'e') { + if (s.charAt(2) == 'u' && s.charAt(1) == 'r') { + id = Id_true; + break L0; + } + } else if (c == 's') { + if (s.charAt(2) == 'i' && s.charAt(1) == 'h') { + id = Id_this; + break L0; + } + } + break L; + case 'v': + X = "void"; + id = Id_void; + break L; + case 'w': + X = "with"; + id = Id_with; + break L; + } + break L; + case 5: + switch (s.charAt(2)) { + case 'a': + X = "class"; + id = Id_class; + break L; + case 'e': + c = s.charAt(0); + if (c == 'b') { + X = "break"; + id = Id_break; + } else if (c == 'y') { + X = "yield"; + id = Id_yield; + } + break L; + case 'i': + X = "while"; + id = Id_while; + break L; + case 'l': + X = "false"; + id = Id_false; + break L; + case 'n': + c = s.charAt(0); + if (c == 'c') { + X = "const"; + id = Id_const; + } else if (c == 'f') { + X = "final"; + id = Id_final; + } + break L; + case 'o': + c = s.charAt(0); + if (c == 'f') { + X = "float"; + id = Id_float; + } else if (c == 's') { + X = "short"; + id = Id_short; + } + break L; + case 'p': + X = "super"; + id = Id_super; + break L; + case 'r': + X = "throw"; + id = Id_throw; + break L; + case 't': + X = "catch"; + id = Id_catch; + break L; + } + break L; + case 6: + switch (s.charAt(1)) { + case 'a': + X = "native"; + id = Id_native; + break L; + case 'e': + c = s.charAt(0); + if (c == 'd') { + X = "delete"; + id = Id_delete; + } else if (c == 'r') { + X = "return"; + id = Id_return; + } + break L; + case 'h': + X = "throws"; + id = Id_throws; + break L; + case 'm': + X = "import"; + id = Id_import; + break L; + case 'o': + X = "double"; + id = Id_double; + break L; + case 't': + X = "static"; + id = Id_static; + break L; + case 'u': + X = "public"; + id = Id_public; + break L; + case 'w': + X = "switch"; + id = Id_switch; + break L; + case 'x': + X = "export"; + id = Id_export; + break L; + case 'y': + X = "typeof"; + id = Id_typeof; + break L; + } + break L; + case 7: + switch (s.charAt(1)) { + case 'a': + X = "package"; + id = Id_package; + break L; + case 'e': + X = "default"; + id = Id_default; + break L; + case 'i': + X = "finally"; + id = Id_finally; + break L; + case 'o': + X = "boolean"; + id = Id_boolean; + break L; + case 'r': + X = "private"; + id = Id_private; + break L; + case 'x': + X = "extends"; + id = Id_extends; + break L; + } + break L; + case 8: + switch (s.charAt(0)) { + case 'a': + X = "abstract"; + id = Id_abstract; + break L; + case 'c': + X = "continue"; + id = Id_continue; + break L; + case 'd': + X = "debugger"; + id = Id_debugger; + break L; + case 'f': + X = "function"; + id = Id_function; + break L; + case 'v': + X = "volatile"; + id = Id_volatile; + break L; + } + break L; + case 9: + c = s.charAt(0); + if (c == 'i') { + X = "interface"; + id = Id_interface; + } else if (c == 'p') { + X = "protected"; + id = Id_protected; + } else if (c == 't') { + X = "transient"; + id = Id_transient; + } + break L; + case 10: + c = s.charAt(1); + if (c == 'm') { + X = "implements"; + id = Id_implements; + } else if (c == 'n') { + X = "instanceof"; + id = Id_instanceof; + } + break L; + case 12: + X = "synchronized"; + id = Id_synchronized; + break L; + } + if (X != null && X != s && !X.equals(s)) + id = 0; + } + // #/generated# + // #/string_id_map# + if (id == 0) { + return Token.EOF; + } + return id & 0xff; + } + + /** + * ECMAScript 6. + */ + private static int stringToKeywordForES(String name, boolean isStrict) { + // #string_id_map# + // The following assumes that Token.EOF == 0 + final int + // 11.6.2.1 Keywords (ECMAScript2015) + Id_break = Token.BREAK, Id_case = Token.CASE, Id_catch = Token.CATCH, Id_class = Token.RESERVED, Id_const = Token.CONST, Id_continue = Token.CONTINUE, Id_debugger = Token.DEBUGGER, Id_default = Token.DEFAULT, Id_delete = Token.DELPROP, Id_do = Token.DO, Id_else = Token.ELSE, Id_export = Token.RESERVED, Id_extends = Token.RESERVED, Id_finally = Token.FINALLY, Id_for = Token.FOR, Id_function = Token.FUNCTION, Id_if = Token.IF, Id_import = Token.RESERVED, Id_in = Token.IN, Id_instanceof = Token.INSTANCEOF, Id_new = Token.NEW, Id_return = Token.RETURN, Id_super = Token.RESERVED, Id_switch = Token.SWITCH, Id_this = Token.THIS, Id_throw = Token.THROW, Id_try = Token.TRY, Id_typeof = Token.TYPEOF, Id_var = Token.VAR, Id_void = Token.VOID, Id_while = Token.WHILE, Id_with = Token.WITH, Id_yield = Token.YIELD, + + // 11.6.2.2 Future Reserved Words + Id_await = Token.RESERVED, Id_enum = Token.RESERVED, + + // 11.6.2.2 NOTE Strict Future Reserved Words + Id_implements = Token.RESERVED, Id_interface = Token.RESERVED, Id_package = Token.RESERVED, Id_private = Token.RESERVED, Id_protected = Token.RESERVED, Id_public = Token.RESERVED, + + // 11.8 Literals + Id_false = Token.FALSE, Id_null = Token.NULL, Id_true = Token.TRUE, + + // Non ReservedWord, but Non IdentifierName in strict mode code. + // 12.1.1 Static Semantics: Early Errors + Id_let = Token.LET, // TODO : Valid IdentifierName in non-strict mode. + Id_static = Token.RESERVED; + + int id; + String s = name; + // #generated# Last update: 2007-04-18 13:53:30 PDT + L0: { + id = 0; + String X = null; + int c; + L: switch (s.length()) { + case 2: + c = s.charAt(1); + if (c == 'f') { + if (s.charAt(0) == 'i') { + id = Id_if; + break L0; + } + } else if (c == 'n') { + if (s.charAt(0) == 'i') { + id = Id_in; + break L0; + } + } else if (c == 'o') { + if (s.charAt(0) == 'd') { + id = Id_do; + break L0; + } + } + break L; + case 3: + switch (s.charAt(0)) { + case 'f': + if (s.charAt(2) == 'r' && s.charAt(1) == 'o') { + id = Id_for; + break L0; + } + break L; + case 'l': + if (s.charAt(2) == 't' && s.charAt(1) == 'e') { + id = Id_let; + break L0; + } + break L; + case 'n': + if (s.charAt(2) == 'w' && s.charAt(1) == 'e') { + id = Id_new; + break L0; + } + break L; + case 't': + if (s.charAt(2) == 'y' && s.charAt(1) == 'r') { + id = Id_try; + break L0; + } + break L; + case 'v': + if (s.charAt(2) == 'r' && s.charAt(1) == 'a') { + id = Id_var; + break L0; + } + break L; + } + break L; + case 4: + switch (s.charAt(0)) { + case 'c': + c = s.charAt(3); + if (c == 'e') { + if (s.charAt(2) == 's' && s.charAt(1) == 'a') { + id = Id_case; + break L0; + } + } + break L; + case 'e': + c = s.charAt(3); + if (c == 'e') { + if (s.charAt(2) == 's' && s.charAt(1) == 'l') { + id = Id_else; + break L0; + } + } else if (c == 'm') { + if (s.charAt(2) == 'u' && s.charAt(1) == 'n') { + id = Id_enum; + break L0; + } + } + break L; + case 'n': + X = "null"; + id = Id_null; + break L; + case 't': + c = s.charAt(3); + if (c == 'e') { + if (s.charAt(2) == 'u' && s.charAt(1) == 'r') { + id = Id_true; + break L0; + } + } else if (c == 's') { + if (s.charAt(2) == 'i' && s.charAt(1) == 'h') { + id = Id_this; + break L0; + } + } + break L; + case 'v': + X = "void"; + id = Id_void; + break L; + case 'w': + X = "with"; + id = Id_with; + break L; + } + break L; + case 5: + switch (s.charAt(2)) { + case 'a': + c = s.charAt(0); + if (c == 'c') { + X = "class"; + id = Id_class; + } else if (c == 'a') { + X = "await"; + id = Id_await; + } + break L; + case 'e': + c = s.charAt(0); + if (c == 'b') { + X = "break"; + id = Id_break; + } else if (c == 'y') { + X = "yield"; + id = Id_yield; + } + break L; + case 'i': + X = "while"; + id = Id_while; + break L; + case 'l': + X = "false"; + id = Id_false; + break L; + case 'n': + X = "const"; + id = Id_const; + break L; + case 'p': + X = "super"; + id = Id_super; + break L; + case 'r': + X = "throw"; + id = Id_throw; + break L; + case 't': + X = "catch"; + id = Id_catch; + break L; + } + break L; + case 6: + switch (s.charAt(1)) { + case 'e': + c = s.charAt(0); + if (c == 'd') { + X = "delete"; + id = Id_delete; + } else if (c == 'r') { + X = "return"; + id = Id_return; + } + break L; + case 'm': + X = "import"; + id = Id_import; + break L; + case 't': + if (isStrict) { + X = "static"; + id = Id_static; + break L; + } + // fallthru + case 'u': + if (isStrict) { + X = "public"; + id = Id_public; + break L; + } + // fallthru + case 'w': + X = "switch"; + id = Id_switch; + break L; + case 'x': + X = "export"; + id = Id_export; + break L; + case 'y': + X = "typeof"; + id = Id_typeof; + break L; + } + break L; + case 7: + switch (s.charAt(1)) { + case 'a': + if (isStrict) { + X = "package"; + id = Id_package; + break L; + } + // fallthru + case 'e': + X = "default"; + id = Id_default; + break L; + case 'i': + X = "finally"; + id = Id_finally; + break L; + case 'r': + if (isStrict) { + X = "private"; + id = Id_private; + break L; + } + // fallthru + case 'x': + X = "extends"; + id = Id_extends; + break L; + } + break L; + case 8: + switch (s.charAt(0)) { + case 'c': + X = "continue"; + id = Id_continue; + break L; + case 'd': + X = "debugger"; + id = Id_debugger; + break L; + case 'f': + X = "function"; + id = Id_function; + break L; + } + break L; + case 9: + c = s.charAt(0); + if (c == 'i' && isStrict) { + X = "interface"; + id = Id_interface; + } else if (c == 'p' && isStrict) { + X = "protected"; + id = Id_protected; + } + break L; + case 10: + c = s.charAt(1); + if (c == 'm' && isStrict) { + X = "implements"; + id = Id_implements; + } else if (c == 'n') { + X = "instanceof"; + id = Id_instanceof; + } + break L; + } + if (X != null && X != s && !X.equals(s)) + id = 0; + } + // #/generated# + // #/string_id_map# + if (id == 0) { + return Token.EOF; + } + return id & 0xff; + } + + final String getSourceString() { + return sourceString; + } + + final int getLineno() { + return lineno; + } + + final String getString() { + return string; + } + + final char getQuoteChar() { + return (char) quoteChar; + } + + final double getNumber() { + return number; + } + + final boolean isNumberBinary() { + return isBinary; + } + + final boolean isNumberOldOctal() { + return isOldOctal; + } + + final boolean isNumberOctal() { + return isOctal; + } + + final boolean isNumberHex() { + return isHex; + } + + final boolean eof() { + return hitEOF; + } + + public final int getToken() throws IOException { + int c; + + retry: for (;;) { + // Eat whitespace, possibly sensitive to newlines. + for (;;) { + c = getChar(); + if (c == EOF_CHAR) { + tokenBeg = cursor - 1; + tokenEnd = cursor; + return Token.EOF; + } else if (c == '\n') { + dirtyLine = false; + tokenBeg = cursor - 1; + tokenEnd = cursor; + return Token.EOL; + } else if (!isJSSpace(c)) { + if (c != '-') { + dirtyLine = true; + } + break; + } + } + + // Assume the token will be 1 char - fixed up below. + tokenBeg = cursor - 1; + tokenEnd = cursor; + + if (c == '@') + return Token.XMLATTR; + + // identifier/keyword/instanceof? + // watch out for starting with a + boolean identifierStart; + boolean isUnicodeEscapeStart = false; + if (c == '\\') { + c = getChar(); + if (c == 'u') { + identifierStart = true; + isUnicodeEscapeStart = true; + stringBufferTop = 0; + } else { + identifierStart = false; + ungetChar(c); + c = '\\'; + } + } else { + identifierStart = Character.isJavaIdentifierStart((char) c); + if (identifierStart) { + stringBufferTop = 0; + addToString(c); + } + } + + if (identifierStart) { + boolean containsEscape = isUnicodeEscapeStart; + for (;;) { + if (isUnicodeEscapeStart) { + // strictly speaking we should probably push-back + // all the bad characters if the uXXXX + // sequence is malformed. But since there isn't a + // correct context(is there?) for a bad Unicode + // escape sequence in an identifier, we can report + // an error here. + int escapeVal = 0; + for (int i = 0; i != 4; ++i) { + c = getChar(); + escapeVal = org.mozilla.javascript.Kit.xDigitToInt(c, escapeVal); + // Next check takes care about c < 0 and bad escape + if (escapeVal < 0) { + break; + } + } + if (escapeVal < 0) { + return Token.ERROR; + } + addToString(escapeVal); + isUnicodeEscapeStart = false; + } else { + c = getChar(); + if (c == '\\') { + c = getChar(); + if (c == 'u') { + isUnicodeEscapeStart = true; + containsEscape = true; + } else { + return Token.ERROR; + } + } else { + if (c == EOF_CHAR + || c == BYTE_ORDER_MARK + || !Character + .isJavaIdentifierPart((char) c)) { + break; + } + addToString(c); + } + } + } + ungetChar(c); + + String str = getStringFromBuffer(); + int result = stringToKeyword(str); + if (result != Token.EOF) { + return result; + } + this.string = (String) allStrings.intern(str); + return Token.NAME; + } + + // is it a number? + if (isDigit(c) || (c == '.' && isDigit(peekChar()))) { + stringBufferTop = 0; + int base = 10; + isHex = isOldOctal = isOctal = isBinary = false; + boolean es6 = false; + + if (c == '0') { + c = getChar(); + if (c == 'x' || c == 'X') { + base = 16; + isHex = true; + c = getChar(); + } else if (es6 && (c == 'o' || c == 'O')) { + base = 8; + isOctal = true; + c = getChar(); + } else if (es6 && (c == 'b' || c == 'B')) { + base = 2; + isBinary = true; + c = getChar(); + } else if (isDigit(c)) { + base = 8; + isOldOctal = true; + } else { + addToString('0'); + } + } + + boolean isEmpty = true; + if (base == 16) { + while (0 <= org.mozilla.javascript.Kit.xDigitToInt(c, 0)) { + addToString(c); + c = getChar(); + isEmpty = false; + } + } else { + while ('0' <= c && c <= '9') { + if (base == 8 && c >= '8') { + if (isOldOctal) { + base = 10; + } else { + return Token.ERROR; + } + } else if (base == 2 && c >= '2') { + return Token.ERROR; + } + addToString(c); + c = getChar(); + isEmpty = false; + } + } + if (isEmpty && (isBinary || isOctal || isHex)) { + return Token.ERROR; + } + + boolean isInteger = true; + + if (base == 10 && (c == '.' || c == 'e' || c == 'E')) { + isInteger = false; + if (c == '.') { + do { + addToString(c); + c = getChar(); + } while (isDigit(c)); + } + if (c == 'e' || c == 'E') { + addToString(c); + c = getChar(); + if (c == '+' || c == '-') { + addToString(c); + c = getChar(); + } + if (!isDigit(c)) { + return Token.ERROR; + } + do { + addToString(c); + c = getChar(); + } while (isDigit(c)); + } + } + ungetChar(c); + String numString = getStringFromBuffer(); + this.string = numString; + + double dval; + if (base == 10 && !isInteger) { + try { + // Use Java conversion to number from string... + dval = Double.parseDouble(numString); + } catch (NumberFormatException ex) { + return Token.ERROR; + } + } else { + dval = stringToNumber(numString, 0, base); + } + + this.number = dval; + return Token.NUMBER; + } + + // is it a string? + if (c == '"' || c == '\'') { + // We attempt to accumulate a string the fast way, by + // building it directly out of the reader. But if there + // are any escaped characters in the string, we revert to + // building it out of a StringBuffer. + + quoteChar = c; + stringBufferTop = 0; + + c = getChar(false); + strLoop: while (c != quoteChar) { + if (c == '\n' || c == EOF_CHAR) { + ungetChar(c); + tokenEnd = cursor; + return Token.ERROR; + } + + if (c == '\\') { + // We've hit an escaped character + int escapeVal; + + c = getChar(); + switch (c) { + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + + // \v a late addition to the ECMA spec, + // it is not in Java, so use 0xb + case 'v': + c = 0xb; + break; + + case 'u': + // Get 4 hex digits; if the u escape is not + // followed by 4 hex digits, use 'u' + the + // literal character sequence that follows. + int escapeStart = stringBufferTop; + addToString('u'); + escapeVal = 0; + for (int i = 0; i != 4; ++i) { + c = getChar(); + escapeVal = org.mozilla.javascript.Kit.xDigitToInt(c, escapeVal); + if (escapeVal < 0) { + continue strLoop; + } + addToString(c); + } + // prepare for replace of stored 'u' sequence + // by escape value + stringBufferTop = escapeStart; + c = escapeVal; + break; + case 'x': + // Get 2 hex digits, defaulting to 'x'+literal + // sequence, as above. + c = getChar(); + escapeVal = org.mozilla.javascript.Kit.xDigitToInt(c, 0); + if (escapeVal < 0) { + addToString('x'); + continue strLoop; + } else { + int c1 = c; + c = getChar(); + escapeVal = org.mozilla.javascript.Kit.xDigitToInt(c, escapeVal); + if (escapeVal < 0) { + addToString('x'); + addToString(c1); + continue strLoop; + } else { + // got 2 hex digits + c = escapeVal; + } + } + break; + + case '\n': + // Remove line terminator after escape to follow + // SpiderMonkey and C/C++ + c = getChar(); + continue strLoop; + + default: + if ('0' <= c && c < '8') { + int val = c - '0'; + c = getChar(); + if ('0' <= c && c < '8') { + val = 8 * val + c - '0'; + c = getChar(); + if ('0' <= c && c < '8' && val <= 037) { + // c is 3rd char of octal sequence only + // if the resulting val <= 0377 + val = 8 * val + c - '0'; + c = getChar(); + } + } + ungetChar(c); + c = val; + } + } + } + addToString(c); + c = getChar(false); + } + + String str = getStringFromBuffer(); + this.string = (String) allStrings.intern(str); + return Token.STRING; + } + + switch (c) { + case ';': + return Token.SEMI; + case '[': + return Token.LB; + case ']': + return Token.RB; + case '{': + return Token.LC; + case '}': + return Token.RC; + case '(': + return Token.LP; + case ')': + return Token.RP; + case ',': + return Token.COMMA; + case '?': + return Token.HOOK; + case ':': + if (matchChar(':')) { + return Token.COLONCOLON; + } else { + return Token.COLON; + } + case '.': + if (matchChar('.')) { + return Token.DOTDOT; + } else if (matchChar('(')) { + return Token.DOTQUERY; + } else { + return Token.DOT; + } + + case '|': + if (matchChar('|')) { + return Token.OR; + } else if (matchChar('=')) { + return Token.ASSIGN_BITOR; + } else { + return Token.BITOR; + } + + case '^': + if (matchChar('=')) { + return Token.ASSIGN_BITXOR; + } else { + return Token.BITXOR; + } + + case '&': + if (matchChar('&')) { + return Token.AND; + } else if (matchChar('=')) { + return Token.ASSIGN_BITAND; + } else { + return Token.BITAND; + } + + case '=': + if (matchChar('=')) { + if (matchChar('=')) { + return Token.SHEQ; + } else { + return Token.EQ; + } + } else if (matchChar('>')) { + return Token.ARROW; + } else { + return Token.ASSIGN; + } + + case '!': + if (matchChar('=')) { + if (matchChar('=')) { + return Token.SHNE; + } else { + return Token.NE; + } + } else { + return Token.NOT; + } + + case '<': + /* NB:treat HTML begin-comment as comment-till-eol */ + if (matchChar('!')) { + if (matchChar('-')) { + if (matchChar('-')) { + tokenBeg = cursor - 4; + skipLine(); + commentType = Token.CommentType.HTML; + return Token.COMMENT; + } + ungetCharIgnoreLineEnd('-'); + } + ungetCharIgnoreLineEnd('!'); + } + if (matchChar('<')) { + if (matchChar('=')) { + return Token.ASSIGN_LSH; + } else { + return Token.LSH; + } + } else { + if (matchChar('=')) { + return Token.LE; + } else { + return Token.LT; + } + } + + case '>': + if (matchChar('>')) { + if (matchChar('>')) { + if (matchChar('=')) { + return Token.ASSIGN_URSH; + } else { + return Token.URSH; + } + } else { + if (matchChar('=')) { + return Token.ASSIGN_RSH; + } else { + return Token.RSH; + } + } + } else { + if (matchChar('=')) { + return Token.GE; + } else { + return Token.GT; + } + } + + case '*': + if (matchChar('=')) { + return Token.ASSIGN_MUL; + } else { + return Token.MUL; + } + + case '/': + // is it a // comment? + if (matchChar('/')) { + tokenBeg = cursor - 2; + skipLine(); + commentType = Token.CommentType.LINE; + return Token.COMMENT; + } + // is it a /* or /** comment? + if (matchChar('*')) { + boolean lookForSlash = false; + tokenBeg = cursor - 2; + if (matchChar('*')) { + lookForSlash = true; + commentType = Token.CommentType.JSDOC; + } else { + commentType = Token.CommentType.BLOCK_COMMENT; + } + for (;;) { + c = getChar(); + if (c == EOF_CHAR) { + tokenEnd = cursor - 1; + return Token.COMMENT; + } else if (c == '*') { + lookForSlash = true; + } else if (c == '/') { + if (lookForSlash) { + tokenEnd = cursor; + return Token.COMMENT; + } + } else { + lookForSlash = false; + tokenEnd = cursor; + } + } + } + + if (matchChar('=')) { + return Token.ASSIGN_DIV; + } else { + return Token.DIV; + } + + case '%': + if (matchChar('=')) { + return Token.ASSIGN_MOD; + } else { + return Token.MOD; + } + + case '~': + return Token.BITNOT; + + case '+': + if (matchChar('=')) { + return Token.ASSIGN_ADD; + } else if (matchChar('+')) { + return Token.INC; + } else { + return Token.ADD; + } + + case '-': + if (matchChar('=')) { + c = Token.ASSIGN_SUB; + } else if (matchChar('-')) { + if (!dirtyLine) { + // treat HTML end-comment after possible whitespace + // after line start as comment-until-eol + if (matchChar('>')) { + skipLine(); + commentType = Token.CommentType.HTML; + return Token.COMMENT; + } + } + c = Token.DEC; + } else { + c = Token.SUB; + } + dirtyLine = true; + return c; + + default: + return Token.ERROR; + } + } + } + + private static boolean isAlpha(int c) { + // Use 'Z' < 'a' + if (c <= 'Z') { + return 'A' <= c; + } else { + return 'a' <= c && c <= 'z'; + } + } + + static boolean isDigit(int c) { + return '0' <= c && c <= '9'; + } + + /* + * As defined in ECMA. jsscan.c uses C isspace() (which allows \v, I think.) + * note that code in getChar() implicitly accepts '\r' == as well. + */ + static boolean isJSSpace(int c) { + if (c <= 127) { + return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB; + } else { + return c == 0xA0 || c == BYTE_ORDER_MARK + || Character.getType((char) c) == Character.SPACE_SEPARATOR; + } + } + + private static boolean isJSFormatChar(int c) { + return c > 127 && Character.getType((char) c) == Character.FORMAT; + } + + /** + * Parser calls the method when it gets / or /= in literal context. + */ + void readRegExp(int startToken) throws IOException { + int start = tokenBeg; + stringBufferTop = 0; + if (startToken == Token.ASSIGN_DIV) { + // Miss-scanned /= + addToString('='); + } else { + if (startToken != Token.DIV) + org.mozilla.javascript.Kit.codeBug(); + } + + boolean inCharSet = false; // true if inside a '['..']' pair + int c; + while ((c = getChar()) != '/' || inCharSet) { + if (c == '\n' || c == EOF_CHAR) { + ungetChar(c); + tokenEnd = cursor - 1; + this.string = new String(stringBuffer, 0, stringBufferTop); + return; + } + if (c == '\\') { + addToString(c); + c = getChar(); + } else if (c == '[') { + inCharSet = true; + } else if (c == ']') { + inCharSet = false; + } + addToString(c); + } + int reEnd = stringBufferTop; + + while (true) { + if (matchChar('g')) + addToString('g'); + else if (matchChar('i')) + addToString('i'); + else if (matchChar('m')) + addToString('m'); + else if (matchChar('y')) // FireFox 3 + addToString('y'); + else + break; + } + tokenEnd = start + stringBufferTop + 2; // include slashes + + if (isAlpha(peekChar())) { + } + + this.string = new String(stringBuffer, 0, reEnd); + this.regExpFlags = new String(stringBuffer, reEnd, stringBufferTop + - reEnd); + } + + String readAndClearRegExpFlags() { + String flags = this.regExpFlags; + this.regExpFlags = null; + return flags; + } + + boolean isXMLAttribute() { + return xmlIsAttribute; + } + + int getFirstXMLToken() throws IOException { + xmlOpenTagsCount = 0; + xmlIsAttribute = false; + xmlIsTagContent = false; + if (!canUngetChar()) + return Token.ERROR; + ungetChar('<'); + return getNextXMLToken(); + } + + int getNextXMLToken() throws IOException { + tokenBeg = cursor; + stringBufferTop = 0; // remember the XML + + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + if (xmlIsTagContent) { + switch (c) { + case '>': + addToString(c); + xmlIsTagContent = false; + xmlIsAttribute = false; + break; + case '/': + addToString(c); + if (peekChar() == '>') { + c = getChar(); + addToString(c); + xmlIsTagContent = false; + xmlOpenTagsCount--; + } + break; + case '{': + ungetChar(c); + this.string = getStringFromBuffer(); + return Token.XML; + case '\'': + case '"': + addToString(c); + if (!readQuotedString(c)) + return Token.ERROR; + break; + case '=': + addToString(c); + xmlIsAttribute = true; + break; + case ' ': + case '\t': + case '\r': + case '\n': + addToString(c); + break; + default: + addToString(c); + xmlIsAttribute = false; + break; + } + + if (!xmlIsTagContent && xmlOpenTagsCount == 0) { + this.string = getStringFromBuffer(); + return Token.XMLEND; + } + } else { + switch (c) { + case '<': + addToString(c); + c = peekChar(); + switch (c) { + case '!': + c = getChar(); // Skip ! + addToString(c); + c = peekChar(); + switch (c) { + case '-': + c = getChar(); // Skip - + addToString(c); + c = getChar(); + if (c == '-') { + addToString(c); + if (!readXmlComment()) + return Token.ERROR; + } else { + // throw away the string in progress + stringBufferTop = 0; + this.string = null; + return Token.ERROR; + } + break; + case '[': + c = getChar(); // Skip [ + addToString(c); + if (getChar() == 'C' && getChar() == 'D' + && getChar() == 'A' && getChar() == 'T' + && getChar() == 'A' && getChar() == '[') { + addToString('C'); + addToString('D'); + addToString('A'); + addToString('T'); + addToString('A'); + addToString('['); + if (!readCDATA()) + return Token.ERROR; + + } else { + // throw away the string in progress + stringBufferTop = 0; + this.string = null; + return Token.ERROR; + } + break; + default: + if (!readEntity()) + return Token.ERROR; + break; + } + break; + case '?': + c = getChar(); // Skip ? + addToString(c); + if (!readPI()) + return Token.ERROR; + break; + case '/': + // End tag + c = getChar(); // Skip / + addToString(c); + if (xmlOpenTagsCount == 0) { + // throw away the string in progress + stringBufferTop = 0; + this.string = null; + return Token.ERROR; + } + xmlIsTagContent = true; + xmlOpenTagsCount--; + break; + default: + // Start tag + xmlIsTagContent = true; + xmlOpenTagsCount++; + break; + } + break; + case '{': + ungetChar(c); + this.string = getStringFromBuffer(); + return Token.XML; + default: + addToString(c); + break; + } + } + } + + tokenEnd = cursor; + stringBufferTop = 0; // throw away the string in progress + this.string = null; + return Token.ERROR; + } + + /** + * + */ + private boolean readQuotedString(int quote) throws IOException { + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + addToString(c); + if (c == quote) + return true; + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + return false; + } + + /** + * + */ + private boolean readXmlComment() throws IOException { + for (int c = getChar(); c != EOF_CHAR;) { + addToString(c); + if (c == '-' && peekChar() == '-') { + c = getChar(); + addToString(c); + if (peekChar() == '>') { + c = getChar(); // Skip > + addToString(c); + return true; + } else { + continue; + } + } + c = getChar(); + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + return false; + } + + /** + * + */ + private boolean readCDATA() throws IOException { + for (int c = getChar(); c != EOF_CHAR;) { + addToString(c); + if (c == ']' && peekChar() == ']') { + c = getChar(); + addToString(c); + if (peekChar() == '>') { + c = getChar(); // Skip > + addToString(c); + return true; + } else { + continue; + } + } + c = getChar(); + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + return false; + } + + /** + * + */ + private boolean readEntity() throws IOException { + int declTags = 1; + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + addToString(c); + switch (c) { + case '<': + declTags++; + break; + case '>': + declTags--; + if (declTags == 0) + return true; + break; + } + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + return false; + } + + /** + * + */ + private boolean readPI() throws IOException { + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + addToString(c); + if (c == '?' && peekChar() == '>') { + c = getChar(); // Skip > + addToString(c); + return true; + } + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + return false; + } + + private String getStringFromBuffer() { + tokenEnd = cursor; + return new String(stringBuffer, 0, stringBufferTop); + } + + private void addToString(int c) { + int N = stringBufferTop; + if (N == stringBuffer.length) { + char[] tmp = new char[stringBuffer.length * 2]; + System.arraycopy(stringBuffer, 0, tmp, 0, N); + stringBuffer = tmp; + } + stringBuffer[N] = (char) c; + stringBufferTop = N + 1; + } + + private boolean canUngetChar() { + return ungetCursor == 0 || ungetBuffer[ungetCursor - 1] != '\n'; + } + + private void ungetChar(int c) { + // can not unread past across line boundary + if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n') + org.mozilla.javascript.Kit.codeBug(); + ungetBuffer[ungetCursor++] = c; + cursor--; + } + + private boolean matchChar(int test) throws IOException { + int c = getCharIgnoreLineEnd(); + if (c == test) { + tokenEnd = cursor; + return true; + } else { + ungetCharIgnoreLineEnd(c); + return false; + } + } + + private int peekChar() throws IOException { + int c = getChar(); + ungetChar(c); + return c; + } + + private int getChar() throws IOException { + return getChar(true); + } + + private int getChar(boolean skipFormattingChars) throws IOException { + if (ungetCursor != 0) { + cursor++; + return ungetBuffer[--ungetCursor]; + } + + for (;;) { + int c; + if (sourceString != null) { + if (sourceCursor == sourceEnd) { + hitEOF = true; + return EOF_CHAR; + } + cursor++; + c = sourceString.charAt(sourceCursor++); + } else { + if (sourceCursor == sourceEnd) { + if (!fillSourceBuffer()) { + hitEOF = true; + return EOF_CHAR; + } + } + cursor++; + c = sourceBuffer[sourceCursor++]; + } + + if (lineEndChar >= 0) { + if (lineEndChar == '\r' && c == '\n') { + lineEndChar = '\n'; + continue; + } + lineEndChar = -1; + lineStart = sourceCursor - 1; + lineno++; + } + + if (c <= 127) { + if (c == '\n' || c == '\r') { + lineEndChar = c; + c = '\n'; + } + } else { + if (c == BYTE_ORDER_MARK) + return c; // BOM is considered whitespace + if (skipFormattingChars && isJSFormatChar(c)) { + continue; + } + if (isJSLineTerminator(c)) { + lineEndChar = c; + c = '\n'; + } + } + return c; + } + } + + private int getCharIgnoreLineEnd() throws IOException { + if (ungetCursor != 0) { + cursor++; + return ungetBuffer[--ungetCursor]; + } + + for (;;) { + int c; + if (sourceString != null) { + if (sourceCursor == sourceEnd) { + hitEOF = true; + return EOF_CHAR; + } + cursor++; + c = sourceString.charAt(sourceCursor++); + } else { + if (sourceCursor == sourceEnd) { + if (!fillSourceBuffer()) { + hitEOF = true; + return EOF_CHAR; + } + } + cursor++; + c = sourceBuffer[sourceCursor++]; + } + + if (c <= 127) { + if (c == '\n' || c == '\r') { + lineEndChar = c; + c = '\n'; + } + } else { + if (c == BYTE_ORDER_MARK) + return c; // BOM is considered whitespace + if (isJSFormatChar(c)) { + continue; + } + if (isJSLineTerminator(c)) { + lineEndChar = c; + c = '\n'; + } + } + return c; + } + } + + private void ungetCharIgnoreLineEnd(int c) { + ungetBuffer[ungetCursor++] = c; + cursor--; + } + + private void skipLine() throws IOException { + // skip to end of line + int c; + while ((c = getChar()) != EOF_CHAR && c != '\n') { + } + ungetChar(c); + tokenEnd = cursor; + } + + /** + * Returns the offset into the current line. + */ + final int getOffset() { + int n = sourceCursor - lineStart; + if (lineEndChar >= 0) { + --n; + } + return n; + } + + private final int charAt(int index) { + if (index < 0) { + return EOF_CHAR; + } + if (sourceString != null) { + if (index >= sourceEnd) { + return EOF_CHAR; + } + return sourceString.charAt(index); + } else { + if (index >= sourceEnd) { + int oldSourceCursor = sourceCursor; + try { + if (!fillSourceBuffer()) { + return EOF_CHAR; + } + } catch (IOException ioe) { + // ignore it, we're already displaying an error... + return EOF_CHAR; + } + // index recalculuation as fillSourceBuffer can move saved + // line buffer and change sourceCursor + index -= (oldSourceCursor - sourceCursor); + } + return sourceBuffer[index]; + } + } + + private final String substring(int beginIndex, int endIndex) { + if (sourceString != null) { + return sourceString.substring(beginIndex, endIndex); + } else { + int count = endIndex - beginIndex; + return new String(sourceBuffer, beginIndex, count); + } + } + + public static boolean isJSLineTerminator(int c) { + // Optimization for faster check for eol character: + // they do not have 0xDFD0 bits set + if ((c & 0xDFD0) != 0) { + return false; + } + return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; + } + + final String getLine() { + int lineEnd = sourceCursor; + if (lineEndChar >= 0) { + // move cursor before newline sequence + lineEnd -= 1; + if (lineEndChar == '\n' && charAt(lineEnd - 1) == '\r') { + lineEnd -= 1; + } + } else { + // Read until the end of line + int lineLength = lineEnd - lineStart; + for (;; ++lineLength) { + int c = charAt(lineStart + lineLength); + if (c == EOF_CHAR || isJSLineTerminator(c)) { + break; + } + } + lineEnd = lineStart + lineLength; + } + return substring(lineStart, lineEnd); + } + + final String getLine(int position, int[] linep) { + assert position >= 0 && position <= cursor; + assert linep.length == 2; + int delta = (cursor + ungetCursor) - position; + int cur = sourceCursor; + if (delta > cur) { + // requested line outside of source buffer + return null; + } + // read back until position + int end = 0, lines = 0; + for (; delta > 0; --delta, --cur) { + assert cur > 0; + int c = charAt(cur - 1); + if (isJSLineTerminator(c)) { + if (c == '\n' && charAt(cur - 2) == '\r') { + // \r\n sequence + delta -= 1; + cur -= 1; + } + lines += 1; + end = cur - 1; + } + } + // read back until line start + int start = 0, offset = 0; + for (; cur > 0; --cur, ++offset) { + int c = charAt(cur - 1); + if (isJSLineTerminator(c)) { + start = cur; + break; + } + } + linep[0] = lineno - lines + (lineEndChar >= 0 ? 1 : 0); + linep[1] = offset; + if (lines == 0) { + return getLine(); + } else { + return substring(start, end); + } + } + + private boolean fillSourceBuffer() throws IOException { + if (sourceString != null) + org.mozilla.javascript.Kit.codeBug(); + if (sourceEnd == sourceBuffer.length) { + if (lineStart != 0 && !isMarkingComment()) { + System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0, + sourceEnd - lineStart); + sourceEnd -= lineStart; + sourceCursor -= lineStart; + lineStart = 0; + } else { + char[] tmp = new char[sourceBuffer.length * 2]; + System.arraycopy(sourceBuffer, 0, tmp, 0, sourceEnd); + sourceBuffer = tmp; + } + } + int n = sourceReader.read(sourceBuffer, sourceEnd, sourceBuffer.length + - sourceEnd); + if (n < 0) { + return false; + } + sourceEnd += n; + return true; + } + + /** + * Return the current position of the scanner cursor. + */ + public int getCursor() { + return cursor; + } + + /** + * Return the absolute source offset of the last scanned token. + */ + public int getTokenBeg() { + return tokenBeg; + } + + /** + * Return the absolute source end-offset of the last scanned token. + */ + public int getTokenEnd() { + return tokenEnd; + } + + /** + * Return tokenEnd - tokenBeg + */ + public int getTokenLength() { + return tokenEnd - tokenBeg; + } + + /** + * Return the type of the last scanned comment. + * + * @return type of last scanned comment, or 0 if none have been scanned. + */ + public Token.CommentType getCommentType() { + return commentType; + } + + private boolean isMarkingComment() { + return commentCursor != -1; + } + + final String getAndResetCurrentComment() { + if (sourceString != null) { + if (isMarkingComment()) + org.mozilla.javascript.Kit.codeBug(); + return sourceString.substring(tokenBeg, tokenEnd); + } else { + if (!isMarkingComment()) + Kit.codeBug(); + StringBuilder comment = new StringBuilder(commentPrefix); + comment.append(sourceBuffer, commentCursor, getTokenLength() + - commentPrefix.length()); + commentCursor = -1; + return comment.toString(); + } + } + + private String convertLastCharToHex(String str) { + int lastIndex = str.length() - 1; + StringBuilder buf = new StringBuilder(str.substring(0, lastIndex)); + buf.append("\\u"); + String hexCode = Integer.toHexString(str.charAt(lastIndex)); + for (int i = 0; i < 4 - hexCode.length(); ++i) { + buf.append('0'); + } + buf.append(hexCode); + return buf.toString(); + } + + // stuff other than whitespace since start of line + private boolean dirtyLine; + + String regExpFlags; + + // Set this to an initial non-null value so that the Parser has + // something to retrieve even if an error has occurred and no + // string is found. Fosters one class of error, but saves lots of + // code. + private String string = ""; + private double number; + private boolean isBinary; + private boolean isOldOctal; + private boolean isOctal; + private boolean isHex; + + // delimiter for last string literal scanned + private int quoteChar; + + private char[] stringBuffer = new char[128]; + private int stringBufferTop; + private ObjToIntMap allStrings = new ObjToIntMap(50); + + // Room to backtrace from to < on failed match of the last - in