editor.cljs 168 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086
  1. (ns ^:no-doc frontend.handler.editor
  2. (:require ["path" :as node-path]
  3. [clojure.set :as set]
  4. [clojure.string :as string]
  5. [clojure.walk :as w]
  6. [dommy.core :as dom]
  7. [electron.ipc :as ipc]
  8. [frontend.commands :as commands]
  9. [frontend.config :as config]
  10. [frontend.date :as date]
  11. [frontend.db :as db]
  12. [frontend.db.async :as db-async]
  13. [frontend.db.file-based.model :as file-model]
  14. [frontend.db.model :as db-model]
  15. [frontend.db.utils :as db-utils]
  16. [frontend.diff :as diff]
  17. [frontend.extensions.pdf.utils :as pdf-utils]
  18. [frontend.format.block :as block]
  19. [frontend.format.mldoc :as mldoc]
  20. [frontend.fs :as fs]
  21. [frontend.handler.assets :as assets-handler]
  22. [frontend.handler.block :as block-handler]
  23. [frontend.handler.common :as common-handler]
  24. [frontend.handler.common.editor :as editor-common-handler]
  25. [frontend.handler.db-based.editor :as db-editor-handler]
  26. [frontend.handler.export.html :as export-html]
  27. [frontend.handler.export.text :as export-text]
  28. [frontend.handler.file-based.editor :as file-editor-handler]
  29. [frontend.handler.file-based.status :as status]
  30. [frontend.handler.notification :as notification]
  31. [frontend.handler.property :as property-handler]
  32. [frontend.handler.property.file :as property-file]
  33. [frontend.handler.property.util :as pu]
  34. [frontend.handler.route :as route-handler]
  35. [frontend.handler.user :as user-handler]
  36. [frontend.mobile.util :as mobile-util]
  37. [frontend.modules.outliner.op :as outliner-op]
  38. [frontend.modules.outliner.tree :as tree]
  39. [frontend.modules.outliner.ui :as ui-outliner-tx]
  40. [frontend.search :as search]
  41. [frontend.state :as state]
  42. [frontend.template :as template]
  43. [frontend.util :as util]
  44. [frontend.util.cursor :as cursor]
  45. [frontend.util.file-based.drawer :as drawer]
  46. [frontend.util.keycode :as keycode]
  47. [frontend.util.list :as list]
  48. [frontend.util.ref :as ref]
  49. [frontend.util.text :as text-util]
  50. [frontend.util.thingatpt :as thingatpt]
  51. [goog.dom :as gdom]
  52. [goog.dom.classes :as gdom-classes]
  53. [goog.object :as gobj]
  54. [goog.string :as gstring]
  55. [lambdaisland.glogi :as log]
  56. [logseq.common.config :as common-config]
  57. [logseq.common.path :as path]
  58. [logseq.common.util :as common-util]
  59. [logseq.common.util.block-ref :as block-ref]
  60. [logseq.common.util.page-ref :as page-ref]
  61. [logseq.db :as ldb]
  62. [logseq.db.common.entity-plus :as entity-plus]
  63. [logseq.db.file-based.schema :as file-schema]
  64. [logseq.db.frontend.asset :as db-asset]
  65. [logseq.db.frontend.property :as db-property]
  66. [logseq.graph-parser.block :as gp-block]
  67. [logseq.graph-parser.mldoc :as gp-mldoc]
  68. [logseq.graph-parser.property :as gp-property]
  69. [logseq.graph-parser.text :as text]
  70. [logseq.graph-parser.utf8 :as utf8]
  71. [logseq.outliner.core :as outliner-core]
  72. [logseq.outliner.property :as outliner-property]
  73. [logseq.shui.dialog.core :as shui-dialog]
  74. [logseq.shui.popup.core :as shui-popup]
  75. [mobile.state :as mobile-state]
  76. [promesa.core :as p]
  77. [rum.core :as rum]))
  78. ;; FIXME: should support multiple images concurrently uploading
  79. (defonce *asset-uploading? (atom false))
  80. (defonce *asset-uploading-process (atom 0))
  81. (def clear-selection! state/clear-selection!)
  82. (def edit-block! block-handler/edit-block!)
  83. (defn- outliner-save-block!
  84. [block & {:as opts}]
  85. (outliner-op/save-block! block opts))
  86. (defn get-block-own-order-list-type
  87. [block]
  88. (pu/lookup block :logseq.property/order-list-type))
  89. (defn set-block-own-order-list-type!
  90. [block type]
  91. (when-let [uuid (:block/uuid block)]
  92. (property-handler/set-block-property! (state/get-current-repo) uuid (pu/get-pid :logseq.property/order-list-type) (name type))))
  93. (defn remove-block-own-order-list-type!
  94. [block]
  95. (when-let [uuid (:block/uuid block)]
  96. (property-handler/remove-block-property! (state/get-current-repo) uuid (pu/get-pid :logseq.property/order-list-type))))
  97. (defn own-order-number-list?
  98. [block]
  99. (when-let [block (db/entity (:db/id block))]
  100. (= (get-block-own-order-list-type block) "number")))
  101. (defn make-block-as-own-order-list!
  102. [block]
  103. (some-> block (set-block-own-order-list-type! "number")))
  104. (defn toggle-blocks-as-own-order-list!
  105. [blocks]
  106. (when (seq blocks)
  107. (let [has-ordered? (some own-order-number-list? blocks)
  108. blocks-uuids (some->> blocks (map :block/uuid) (remove nil?))
  109. order-list-prop (pu/get-pid :logseq.property/order-list-type)
  110. repo (state/get-current-repo)]
  111. (if has-ordered?
  112. (property-handler/batch-remove-block-property! repo blocks-uuids order-list-prop)
  113. (property-handler/batch-set-block-property! repo blocks-uuids order-list-prop "number")))))
  114. (defn get-selection-and-format
  115. []
  116. (when-let [block (state/get-edit-block)]
  117. (when (:block/uuid block)
  118. (when-let [edit-id (state/get-edit-input-id)]
  119. (when-let [input (gdom/getElement edit-id)]
  120. (let [selection-start (util/get-selection-start input)
  121. selection-end (util/get-selection-end input)
  122. value (gobj/get input "value")
  123. selection (when (not= selection-start selection-end)
  124. (subs value selection-start selection-end))
  125. selection-start (+ selection-start
  126. (count (take-while #(= " " %) selection)))
  127. selection-end (- selection-end
  128. (count (take-while #(= " " %) (reverse selection))))]
  129. {:selection-start selection-start
  130. :selection-end selection-end
  131. :selection (some-> selection
  132. string/trim)
  133. :format (get block :block/format :markdown)
  134. :value value
  135. :block block
  136. :edit-id edit-id
  137. :input input}))))))
  138. (defn- format-text!
  139. [pattern-fn]
  140. (when-let [m (get-selection-and-format)]
  141. (let [{:keys [selection-start selection-end format selection value edit-id input]} m
  142. pattern (pattern-fn format)
  143. pattern-count (count pattern)
  144. pattern-prefix (subs value (max 0 (- selection-start pattern-count)) selection-start)
  145. pattern-suffix (subs value selection-end (min (count value) (+ selection-end pattern-count)))
  146. already-wrapped? (= pattern pattern-prefix pattern-suffix)
  147. prefix (if already-wrapped?
  148. (subs value 0 (- selection-start pattern-count))
  149. (subs value 0 selection-start))
  150. postfix (if already-wrapped?
  151. (subs value (+ selection-end pattern-count))
  152. (subs value selection-end))
  153. inner-value (cond-> selection
  154. (not already-wrapped?)
  155. (#(str pattern % pattern)))
  156. new-value (str prefix inner-value postfix)]
  157. (state/set-edit-content! edit-id new-value)
  158. (cond
  159. already-wrapped? (cursor/set-selection-to input (- selection-start pattern-count) (- selection-end pattern-count))
  160. selection (cursor/move-cursor-to input (+ selection-end pattern-count))
  161. :else (cursor/set-selection-to input (+ selection-start pattern-count) (+ selection-end pattern-count))))))
  162. (defn bold-format! []
  163. (format-text! config/get-bold))
  164. (defn italics-format! []
  165. (format-text! config/get-italic))
  166. (defn highlight-format! []
  167. (when-let [block (state/get-edit-block)]
  168. (let [format (get block :block/format :markdown)]
  169. (format-text! #(config/get-highlight format)))))
  170. (defn strike-through-format! []
  171. (format-text! config/get-strike-through))
  172. (defn html-link-format!
  173. ([]
  174. (html-link-format! nil))
  175. ([text]
  176. (when-let [m (get-selection-and-format)]
  177. (let [{:keys [selection-start selection-end format selection value edit-id input]} m
  178. empty-selection? (= selection-start selection-end)
  179. selection-link? (and selection (gp-mldoc/mldoc-link? format selection))
  180. [content forward-pos] (cond
  181. empty-selection?
  182. (config/get-empty-link-and-forward-pos format)
  183. (and text selection-link?)
  184. (config/with-label-link format text selection)
  185. text
  186. (config/with-label-link format selection text)
  187. selection-link?
  188. (config/with-default-link format selection)
  189. :else
  190. (config/with-default-label format selection))
  191. new-value (str
  192. (subs value 0 selection-start)
  193. content
  194. (subs value selection-end))
  195. cur-pos (or selection-start (cursor/pos input))]
  196. (state/set-edit-content! edit-id new-value)
  197. (cursor/move-cursor-to input (+ cur-pos forward-pos))))))
  198. (defn open-block-in-sidebar!
  199. [block-id]
  200. ; (assert (uuid? block-id) "frontend.handler.editor/open-block-in-sidebar! expects block-id to be of type uuid")
  201. (when block-id
  202. (when-let [block (db/entity (if (number? block-id) block-id [:block/uuid block-id]))]
  203. (let [page? (nil? (:block/page block))]
  204. (state/sidebar-add-block!
  205. (state/get-current-repo)
  206. (:db/id block)
  207. (if page? :page :block))))))
  208. (defn reset-cursor-range!
  209. [node]
  210. (when node
  211. (state/set-cursor-range! (util/caret-range node))))
  212. (defn restore-cursor-pos!
  213. [id markup]
  214. (when-let [node (gdom/getElement (str id))]
  215. (let [cursor-range (state/get-cursor-range)
  216. pos (or (state/get-editor-last-pos)
  217. (and cursor-range
  218. (diff/find-position markup cursor-range)))]
  219. (cursor/move-cursor-to node pos))))
  220. (defn highlight-block!
  221. [block-uuid]
  222. (let [blocks (util/get-blocks-by-id block-uuid)]
  223. (doseq [block blocks]
  224. (dom/add-class! block "block-highlight"))))
  225. (defn unhighlight-blocks!
  226. []
  227. (let [blocks (some->> (array-seq (js/document.getElementsByClassName "block-highlight"))
  228. (repeat 2)
  229. (apply concat))]
  230. (doseq [block blocks]
  231. (gdom-classes/remove block "block-highlight"))))
  232. (defn wrap-parse-block
  233. [block]
  234. (if (config/db-based-graph? (state/get-current-repo))
  235. (db-editor-handler/wrap-parse-block block)
  236. (file-editor-handler/wrap-parse-block block)))
  237. (defn- save-block-inner!
  238. [block value opts]
  239. (let [block {:db/id (:db/id block)
  240. :block/uuid (:block/uuid block)
  241. :block/title value}
  242. block' (-> (wrap-parse-block block)
  243. ;; :block/uuid might be changed when backspace/delete
  244. ;; a block that has been refed
  245. (assoc :block/uuid (:block/uuid block)))
  246. opts' (assoc opts :outliner-op :save-block)]
  247. (ui-outliner-tx/transact!
  248. opts'
  249. (outliner-save-block! block'))))
  250. ;; id: block dom id, "ls-block-counter-uuid"
  251. (defn- another-block-with-same-id-exists?
  252. [current-id block-id]
  253. (when-let [id (and (string? block-id) (parse-uuid block-id))]
  254. (and (not= current-id id)
  255. (db/entity [:block/uuid id]))))
  256. (defn save-block-if-changed!
  257. ([block value]
  258. (save-block-if-changed! block value nil))
  259. ([block value
  260. {:keys [force?]
  261. :as opts}]
  262. (let [{:block/keys [uuid format repo title]} block
  263. format (or format :markdown)
  264. repo (or repo (state/get-current-repo))
  265. format (or format (state/get-preferred-format))
  266. block-id (let [properties (:block/properties block)]
  267. (when (and (not (config/db-based-graph? repo)) (map? properties))
  268. (get properties :id)))
  269. content (if (config/db-based-graph? repo)
  270. (:block/title (db/entity (:db/id block)))
  271. (-> (property-file/remove-built-in-properties-when-file-based repo format title)
  272. (drawer/remove-logbook)))]
  273. (cond
  274. (another-block-with-same-id-exists? uuid block-id)
  275. (notification/show!
  276. [:p.content
  277. (util/format "Block with the id %s already exists!" block-id)]
  278. :error)
  279. force?
  280. (save-block-inner! block value opts)
  281. :else
  282. (when content
  283. (let [content-changed? (not= (string/trim content) (string/trim value))]
  284. (when content-changed?
  285. (save-block-inner! block value opts))))))))
  286. (defn- compute-fst-snd-block-text
  287. [value selection-start selection-end]
  288. (when (string? value)
  289. (let [fst-block-text (subs value 0 selection-start)
  290. snd-block-text (string/triml (subs value selection-end))]
  291. [fst-block-text snd-block-text])))
  292. (declare save-current-block!)
  293. (defn outliner-insert-block!
  294. [config current-block new-block {:keys [sibling? keep-uuid? ordered-list?
  295. replace-empty-target?]}]
  296. (let [ref-query-top-block? (and (or (:ref? config)
  297. (:custom-query? config))
  298. (not (:ref-query-child? config)))
  299. has-children? (db/has-children? (:block/uuid current-block))
  300. library? (:library? config)
  301. sibling? (cond
  302. ref-query-top-block?
  303. false
  304. (and library? (ldb/page? current-block))
  305. true
  306. (boolean? sibling?)
  307. sibling?
  308. (util/collapsed? current-block)
  309. true
  310. :else
  311. (not has-children?))
  312. new-block' (if library?
  313. (-> new-block
  314. (-> (assoc :block/tags #{:logseq.class/Page}
  315. :block/name (util/page-name-sanity-lc (:block/title new-block)))
  316. (dissoc :block/page)))
  317. new-block)]
  318. (ui-outliner-tx/transact!
  319. {:outliner-op :insert-blocks}
  320. (save-current-block! {:current-block current-block})
  321. (outliner-op/insert-blocks! [new-block'] current-block {:sibling? sibling?
  322. :keep-uuid? keep-uuid?
  323. :ordered-list? ordered-list?
  324. :replace-empty-target? replace-empty-target?}))))
  325. (defn- block-self-alone-when-insert?
  326. [config uuid]
  327. (let [current-page (state/get-current-page)
  328. block-id (or (some-> (:id config) parse-uuid)
  329. (some-> current-page parse-uuid))]
  330. (= uuid block-id)))
  331. (defn insert-new-block-before-block-aux!
  332. [config block value]
  333. (let [edit-input-id (state/get-edit-input-id)
  334. input (gdom/getElement edit-input-id)
  335. input-text-selected? (util/input-text-selected? input)
  336. new-m {:block/uuid (db/new-block-id)
  337. :block/title ""}
  338. prev-block (-> (merge (select-keys block [:block/parent :block/format :block/page])
  339. new-m)
  340. (wrap-parse-block))
  341. block' (db/entity (:db/id block))
  342. left-or-parent (or (ldb/get-left-sibling block') (:block/parent block'))]
  343. (when input-text-selected?
  344. (let [selection-start (util/get-selection-start input)
  345. selection-end (util/get-selection-end input)
  346. [_ new-content] (compute-fst-snd-block-text value selection-start selection-end)]
  347. (state/set-edit-content! edit-input-id new-content)))
  348. (let [sibling? (not= (:db/id left-or-parent) (:db/id (:block/parent block)))
  349. result (outliner-insert-block! config left-or-parent prev-block {:sibling? sibling?
  350. :keep-uuid? true})]
  351. [result sibling? prev-block])))
  352. (defn insert-new-block-aux!
  353. [config
  354. {:block/keys [uuid]
  355. :as block}
  356. value]
  357. (let [block-self? (block-self-alone-when-insert? config uuid)
  358. input (gdom/getElement (state/get-edit-input-id))
  359. selection-start (util/get-selection-start input)
  360. selection-end (util/get-selection-end input)
  361. [fst-block-text snd-block-text] (compute-fst-snd-block-text value selection-start selection-end)
  362. current-block (assoc block :block/title fst-block-text)
  363. current-block (apply dissoc current-block file-schema/retract-attributes)
  364. new-m {:block/uuid (db/new-block-id)
  365. :block/title snd-block-text}
  366. next-block (-> (merge (select-keys block [:block/parent :block/format :block/page])
  367. new-m)
  368. (wrap-parse-block))
  369. sibling? (or (:block/collapsed? (:block/link block)) (when block-self? false))
  370. result (outliner-insert-block! config current-block next-block {:sibling? sibling?
  371. :keep-uuid? true})]
  372. [result sibling? next-block]))
  373. (defn clear-when-saved!
  374. []
  375. (commands/restore-state))
  376. (defn get-state
  377. []
  378. (let [[{:keys [on-hide block block-id block-parent-id format sidebar?]} id config] (state/get-editor-args)
  379. node (gdom/getElement id)]
  380. (when node
  381. (let [value (gobj/get node "value")
  382. pos (util/get-selection-start node)]
  383. {:config config
  384. :on-hide on-hide
  385. :sidebar? sidebar?
  386. :format format
  387. :id id
  388. :block (or (db/entity [:block/uuid (:block/uuid block)]) block)
  389. :block-id block-id
  390. :block-parent-id block-parent-id
  391. :node node
  392. :value value
  393. :pos pos
  394. :block-container (util/rec-get-node node "ls-block")}))))
  395. (defn- get-node-container-id
  396. [node]
  397. (some-> (dom/attr node "containerid")
  398. util/safe-parse-int))
  399. (defn- get-node-parent
  400. [node]
  401. (some-> (gobj/get node "parentNode")
  402. (util/rec-get-node "ls-block")))
  403. (defn- get-node-prev-sibling
  404. [node]
  405. (let [parent (gobj/get node "parentNode")]
  406. (if (dom/attr parent "data-index")
  407. (some-> (.-previousSibling parent)
  408. (dom/sel1 ".ls-block"))
  409. (.-previousSibling node))))
  410. (defn- get-node-next-sibling
  411. [node]
  412. (let [parent (gobj/get node "parentNode")]
  413. (if (dom/attr parent "data-index")
  414. (some-> (.-nextSibling parent)
  415. (dom/sel1 ".ls-block"))
  416. (.-nextSibling node))))
  417. (defn- get-new-container-id
  418. [op data]
  419. (let [{:keys [block block-container]} (get-state)]
  420. (when block
  421. (let [node block-container
  422. linked? (some? (dom/attr node "originalblockid"))]
  423. (case op
  424. :insert
  425. (when (and linked? (not (false? (:sibling? data))))
  426. (some-> (util/rec-get-node node "blocks-container")
  427. get-node-container-id))
  428. :indent
  429. ;; Get prev sibling's container id
  430. (when-let [prev (get-node-prev-sibling node)]
  431. (when (dom/attr prev "originalblockid")
  432. (get-node-container-id prev)))
  433. :move-up
  434. (let [parent (get-node-parent node)
  435. prev (when parent (get-node-prev-sibling parent))]
  436. (when (and prev (dom/attr prev "originalblockid"))
  437. (get-node-container-id prev)))
  438. :move-down
  439. (let [parent (get-node-parent node)
  440. next (when parent (get-node-next-sibling parent))]
  441. (when (and next (dom/attr next "originalblockid"))
  442. (get-node-container-id next)))
  443. :outdent
  444. ;; Get embed block's root container id
  445. (when-let [parent (some-> (gobj/get node "parentNode")
  446. (util/rec-get-node "ls-block"))]
  447. (when (dom/attr parent "originalblockid")
  448. (some-> (util/rec-get-node parent "blocks-container")
  449. get-node-container-id))))))))
  450. (defn insert-new-block!
  451. "Won't save previous block content - remember to save!"
  452. ([state]
  453. (insert-new-block! state nil))
  454. ([_state block-value]
  455. (->
  456. (when (not config/publishing?)
  457. (when-let [state (get-state)]
  458. (state/set-state! :editor/async-unsaved-chars "")
  459. (let [{:keys [block value config]} state
  460. value (if (string? block-value) block-value value)
  461. block-id (:block/uuid block)
  462. block-self? (block-self-alone-when-insert? config block-id)
  463. input-id (state/get-edit-input-id)
  464. input (gdom/getElement input-id)
  465. selection-start (util/get-selection-start input)
  466. selection-end (util/get-selection-end input)
  467. [fst-block-text snd-block-text] (compute-fst-snd-block-text value selection-start selection-end)
  468. insert-above? (and (string/blank? fst-block-text) (not (string/blank? snd-block-text)))
  469. block' (or (db/entity [:block/uuid block-id]) block)
  470. original-block (:original-block config)
  471. block'' (or
  472. (when original-block
  473. (let [e (db/entity (:db/id block'))]
  474. (if (and (some? (first (:block/_parent e)))
  475. (not (:block/collapsed? e)))
  476. ;; object has children and not collapsed
  477. block'
  478. original-block)))
  479. block')
  480. insert-fn (cond
  481. block-self?
  482. insert-new-block-aux!
  483. insert-above?
  484. insert-new-block-before-block-aux!
  485. :else
  486. insert-new-block-aux!)
  487. [result-promise sibling? next-block] (insert-fn config block'' value)
  488. edit-block-f (fn []
  489. (let [next-block' (db/entity [:block/uuid (:block/uuid next-block)])
  490. pos 0
  491. unsaved-chars @(:editor/async-unsaved-chars @state/state)
  492. container-id (get-new-container-id :insert {:sibling? sibling?})]
  493. (edit-block! next-block' (+ pos (count unsaved-chars))
  494. {:container-id container-id
  495. :custom-content (str unsaved-chars (:block/title next-block'))})))]
  496. (p/do!
  497. (state/set-state! :editor/edit-block-fn edit-block-f)
  498. result-promise
  499. (clear-when-saved!)))))
  500. (p/finally (fn []
  501. (state/set-state! :editor/async-unsaved-chars nil))))))
  502. (defn api-insert-new-block!
  503. [content {:keys [page block-uuid
  504. sibling? before? start? end?
  505. properties
  506. custom-uuid replace-empty-target? edit-block? ordered-list? other-attrs]
  507. :or {sibling? false
  508. before? false
  509. edit-block? true}
  510. :as config}]
  511. (when (or page block-uuid)
  512. (let [repo (state/get-current-repo)
  513. db-based? (config/db-based-graph? repo)
  514. before? (if page false before?)
  515. sibling? (boolean sibling?)
  516. sibling? (if before? true (if page false sibling?))
  517. block (if page
  518. (db/get-page page)
  519. (db/entity [:block/uuid block-uuid]))]
  520. (when block
  521. (let [last-block (when (not sibling?)
  522. (let [children (:block/_parent block)
  523. blocks (db/sort-by-order children)
  524. last-block-id (:db/id (last blocks))]
  525. (when last-block-id
  526. (db/entity last-block-id))))
  527. format (or
  528. (get block :block/format :markdown)
  529. (db/get-page-format (:block/name block))
  530. (state/get-preferred-format))
  531. content (if (and (not db-based?) (seq properties))
  532. (property-file/insert-properties-when-file-based repo format content properties)
  533. content)
  534. new-block (cond->
  535. (-> (select-keys block [:block/page])
  536. (assoc :block/title content))
  537. (not db-based?)
  538. (assoc :block/format format))
  539. new-block (assoc new-block :block/page
  540. (if page
  541. (:db/id block)
  542. (:db/id (:block/page new-block))))
  543. new-block (-> new-block
  544. (wrap-parse-block)
  545. (assoc :block/uuid (or custom-uuid (db/new-block-id))))
  546. new-block (merge new-block other-attrs)
  547. block' (db/entity (:db/id block))
  548. [target-block sibling?] (cond
  549. before?
  550. (let [left-or-parent (or (ldb/get-left-sibling block)
  551. (:block/parent block))
  552. sibling? (if (= (:db/id (:block/parent block)) (:db/id left-or-parent))
  553. false sibling?)]
  554. [left-or-parent sibling?])
  555. sibling?
  556. [block' sibling?]
  557. start?
  558. [block' false]
  559. end?
  560. (if last-block
  561. [block' false]
  562. [last-block true])
  563. last-block
  564. [last-block true]
  565. block
  566. [block' sibling?]
  567. ;; FIXME: assert
  568. :else
  569. nil)]
  570. (when target-block
  571. (p/do!
  572. (let [new-block' (if (and db-based? (seq properties))
  573. (into new-block properties)
  574. new-block)]
  575. (ui-outliner-tx/transact!
  576. {:outliner-op :insert-blocks}
  577. (outliner-insert-block! config target-block new-block'
  578. {:sibling? sibling?
  579. :keep-uuid? true
  580. :ordered-list? ordered-list?
  581. :replace-empty-target? replace-empty-target?})))
  582. (when edit-block?
  583. (if (and replace-empty-target?
  584. (string/blank? (:block/title last-block)))
  585. (edit-block! last-block :max)
  586. (edit-block! new-block :max)))
  587. (when-let [id (:block/uuid new-block)]
  588. (db/entity [:block/uuid id])))))))))
  589. (defn check
  590. [{:block/keys [marker title repeated? uuid] :as block}]
  591. (let [new-content (string/replace-first title marker "DONE")
  592. new-content (if repeated?
  593. (file-editor-handler/update-timestamps-content! block title)
  594. new-content)
  595. input-id (state/get-edit-input-id)]
  596. (if (and input-id
  597. (string/ends-with? input-id (str uuid)))
  598. (state/set-edit-content! input-id new-content)
  599. (save-block-if-changed! block new-content))))
  600. (defn uncheck
  601. [{:block/keys [title uuid] :as block}]
  602. (let [marker (if (= :now (state/get-preferred-workflow))
  603. "LATER"
  604. "TODO")
  605. new-content (string/replace-first title "DONE" marker)
  606. input-id (state/get-edit-input-id)]
  607. (if (and input-id
  608. (string/ends-with? input-id (str uuid)))
  609. (state/set-edit-content! input-id new-content)
  610. (save-block-if-changed! block new-content))))
  611. (defn get-selected-blocks
  612. []
  613. (distinct (seq (state/get-selection-blocks))))
  614. (defn set-marker
  615. "The set-marker will set a new marker on the selected block.
  616. if the `new-marker` is nil, it will generate it automatically."
  617. ([block]
  618. (set-marker block nil))
  619. ([{:block/keys [marker title format] :as block} new-marker]
  620. (let [[new-content _] (status/cycle-marker title marker new-marker format (state/get-preferred-workflow))]
  621. (save-block-if-changed! block new-content))))
  622. (defn file-based-cycle-todo!
  623. [block]
  624. (when (not-empty (:block/title block))
  625. (set-marker block)))
  626. (defn db-based-cycle-todo!
  627. [block]
  628. (let [status-value (if (ldb/class-instance? (db/entity :logseq.class/Task) block)
  629. (:logseq.property/status block)
  630. (get block :logseq.property/status {}))
  631. next-status (case (:db/ident status-value)
  632. :logseq.property/status.todo
  633. :logseq.property/status.doing
  634. :logseq.property/status.doing
  635. :logseq.property/status.done
  636. :logseq.property/status.done
  637. nil
  638. :logseq.property/status.todo)
  639. repo (state/get-current-repo)]
  640. (property-handler/set-block-property! repo (:block/uuid block)
  641. :logseq.property/status
  642. (:db/id (db/entity next-status)))))
  643. (defn cycle-todos!
  644. []
  645. (when-let [blocks (seq (get-selected-blocks))]
  646. (let [db-based? (config/db-based-graph? (state/get-current-repo))
  647. ids (->> (distinct (map #(when-let [id (dom/attr % "blockid")]
  648. (uuid id)) blocks))
  649. (remove nil?))]
  650. (ui-outliner-tx/transact!
  651. {:outliner-op :cycle-todos}
  652. (doseq [id ids]
  653. (when-let [block (db/entity [:block/uuid id])]
  654. (if db-based?
  655. (db-based-cycle-todo! block)
  656. (file-based-cycle-todo! block))))))))
  657. (defn cycle-todo!
  658. []
  659. #_:clj-kondo/ignore
  660. (when-not (state/get-editor-action)
  661. (if-let [blocks (seq (get-selected-blocks))]
  662. (cycle-todos!)
  663. (when-let [edit-block (state/get-edit-block)]
  664. (let [edit-input-id (state/get-edit-input-id)
  665. current-input (gdom/getElement edit-input-id)]
  666. (if (config/db-based-graph? (state/get-current-repo))
  667. (when-let [block (db/entity (:db/id edit-block))]
  668. (let [pos (state/get-edit-pos)]
  669. (p/do!
  670. (ui-outliner-tx/transact!
  671. {:outliner-op :cycle-todos}
  672. (db-based-cycle-todo! block))
  673. ;; FIXME: don't change current editor's position
  674. )))
  675. (let [content (state/get-edit-content)
  676. format (or (db/get-page-format (state/get-current-page))
  677. (state/get-preferred-format))
  678. [new-content marker] (status/cycle-marker content nil nil format (state/get-preferred-workflow))
  679. new-pos (commands/compute-pos-delta-when-change-marker
  680. content marker (cursor/pos current-input))]
  681. (state/set-edit-content! edit-input-id new-content)
  682. (cursor/move-cursor-to current-input new-pos))))))))
  683. (defn set-priority
  684. [{:block/keys [priority title] :as block} new-priority]
  685. (when-not (config/db-based-graph? (state/get-current-repo))
  686. (let [new-content (string/replace-first title
  687. (util/format "[#%s]" priority)
  688. (util/format "[#%s]" new-priority))]
  689. (save-block-if-changed! block new-content))))
  690. (defn delete-block-aux!
  691. [{:block/keys [uuid] :as _block}]
  692. (let [block (db/entity [:block/uuid uuid])]
  693. (when block
  694. (let [blocks (block-handler/get-top-level-blocks [block])]
  695. (ui-outliner-tx/transact!
  696. {:outliner-op :delete-blocks}
  697. (outliner-op/delete-blocks! blocks {}))))))
  698. (defn- move-to-prev-block
  699. [repo sibling-block format value]
  700. (when (and repo sibling-block)
  701. (when-let [sibling-block-id (dom/attr sibling-block "blockid")]
  702. (when-let [sibling-entity (db/entity [:block/uuid (uuid sibling-block-id)])]
  703. (if (:block/name sibling-entity)
  704. {:prev-block sibling-entity
  705. :new-value (:block/title sibling-entity)
  706. :edit-block-f #(edit-block! sibling-entity :max)}
  707. (let [db? (config/db-based-graph? repo)
  708. original-content (if (= (:db/id sibling-entity) (:db/id (state/get-edit-block)))
  709. (state/get-edit-content)
  710. (:block/title sibling-entity))
  711. value' (if db?
  712. original-content
  713. (-> (property-file/remove-built-in-properties-when-file-based repo format original-content)
  714. (drawer/remove-logbook)))
  715. value (if db?
  716. value
  717. (->> value
  718. (property-file/remove-properties-when-file-based repo format)
  719. (drawer/remove-logbook)))
  720. new-value (str value' value)
  721. tail-len (count value)
  722. pos (max
  723. (if original-content
  724. (gobj/get (utf8/encode original-content) "length")
  725. 0)
  726. 0)
  727. [edit-target container-id] [(db/entity (:db/id sibling-entity)) (some-> (dom/attr sibling-block "containerid")
  728. util/safe-parse-int)]]
  729. {:prev-block sibling-entity
  730. :new-content new-value
  731. :pos pos
  732. :edit-block-f #(edit-block! edit-target
  733. pos
  734. {:custom-content new-value
  735. :tail-len tail-len
  736. :container-id container-id})}))))))
  737. (declare save-block!)
  738. (declare expand-block!)
  739. (defn- one-page-another-block
  740. [block1 block2]
  741. (and
  742. (not (every? ldb/page? [block1 block2]))
  743. (or (ldb/page? block1)
  744. (ldb/page? block2))))
  745. (defn delete-block-inner!
  746. [repo {:keys [block-id value format config block-container current-block next-block delete-concat?]}]
  747. (when (and block-id (not (one-page-another-block current-block next-block)))
  748. (when-let [block-e (db/entity [:block/uuid block-id])]
  749. (let [prev-block (db-model/get-prev (db/get-db) (:db/id block-e))
  750. input-empty? (string/blank? (state/get-edit-content))]
  751. (cond
  752. (and (nil? prev-block)
  753. (nil? (:block/parent block-e)))
  754. nil
  755. :else
  756. (let [has-children? (seq (:block/_parent block-e))
  757. block (db/entity (:db/id block-e))
  758. left (or (ldb/get-left-sibling block) (:block/parent block))
  759. left-has-children? (and left
  760. (when-let [block-id (:block/uuid left)]
  761. (let [block (db/entity [:block/uuid block-id])]
  762. (seq (:block/_parent block)))))]
  763. (when-not (and has-children? left-has-children?)
  764. (let [block-parent block-container
  765. sibling-or-parent-block
  766. (if (:embed? config)
  767. (util/get-prev-block-non-collapsed
  768. block-parent
  769. {:container (util/rec-get-blocks-container block-parent)})
  770. (util/get-prev-block-non-collapsed-non-embed block-parent))
  771. {:keys [prev-block new-content edit-block-f]}
  772. (move-to-prev-block repo sibling-or-parent-block format value)
  773. concat-prev-block? (boolean (and prev-block new-content))
  774. transact-opts {:outliner-op :delete-blocks}]
  775. (cond
  776. (and prev-block (:block/name prev-block)
  777. (not= (:db/id prev-block) (:db/id (:block/parent block)))
  778. (db-model/hidden-page? (:block/page block))) ; embed page
  779. nil
  780. (and concat-prev-block? input-empty? delete-concat?)
  781. (let [children (:block/_parent (db/entity (:db/id current-block)))] ; del
  782. (p/do!
  783. (ui-outliner-tx/transact!
  784. transact-opts
  785. (when (seq children)
  786. (outliner-op/move-blocks!
  787. (remove (fn [c] (= (:db/id c) (:db/id next-block))) children)
  788. next-block
  789. {:sibling? false}))
  790. (when (= (:db/id current-block) (:db/id (:block/parent next-block)))
  791. (outliner-op/move-blocks!
  792. [next-block]
  793. current-block
  794. {:sibling? true}))
  795. (delete-block-aux! current-block))
  796. (edit-block! (db/entity (:db/id next-block)) 0)))
  797. (and concat-prev-block? (string/blank? (:block/title prev-block)) (not delete-concat?)) ; backspace
  798. (p/do!
  799. (ui-outliner-tx/transact!
  800. transact-opts
  801. (when-not (= (:db/id (:block/parent block)) (:db/id (:block/parent prev-block)))
  802. (outliner-op/move-blocks!
  803. [block]
  804. prev-block
  805. {:sibling? true}))
  806. (delete-block-aux! prev-block))
  807. (edit-block! (db/entity (:db/id current-block)) 0))
  808. concat-prev-block?
  809. (let [children (:block/_parent (db/entity (:db/id block)))]
  810. (p/do!
  811. (state/set-state! :editor/edit-block-fn edit-block-f)
  812. (ui-outliner-tx/transact!
  813. transact-opts
  814. (when (seq children)
  815. (outliner-op/move-blocks! children prev-block {:sibling? false}))
  816. (delete-block-aux! block)
  817. (save-block! repo prev-block new-content {}))))
  818. :else
  819. (p/do!
  820. (state/set-state! :editor/edit-block-fn edit-block-f)
  821. (delete-block-aux! block)))))))))))
  822. (defn move-blocks!
  823. [blocks target opts]
  824. (when (seq blocks)
  825. (ui-outliner-tx/transact!
  826. {:outliner-op :move-blocks}
  827. (outliner-op/move-blocks! blocks target opts))))
  828. (defn move-selected-blocks
  829. [e]
  830. (util/stop e)
  831. (let [block-ids (or (seq (state/get-selection-block-ids))
  832. (when-let [id (:block/uuid (state/get-edit-block))]
  833. [id]))]
  834. (if (seq block-ids)
  835. (let [blocks (->> (map (fn [id] (db/entity [:block/uuid id])) block-ids)
  836. block-handler/get-top-level-blocks)]
  837. (route-handler/go-to-search! :nodes
  838. {:action :move-blocks
  839. :blocks blocks
  840. :trigger (fn [chosen]
  841. (state/pub-event! [:editor/hide-action-bar])
  842. (state/clear-selection!)
  843. (move-blocks! blocks (:source-block chosen) {:bottom? true}))}))
  844. (notification/show! "There's no block selected, please select blocks first." :warning))))
  845. (defn delete-block!
  846. [repo]
  847. (delete-block-inner! repo (get-state)))
  848. (defn delete-blocks!
  849. [repo block-uuids blocks dom-blocks mobile-action-bar?]
  850. (when (seq block-uuids)
  851. (let [uuid->dom-block (zipmap block-uuids dom-blocks)
  852. block (first blocks)
  853. block-parent (get uuid->dom-block (:block/uuid block))
  854. sibling-block (when block-parent (util/get-prev-block-non-collapsed-non-embed block-parent))
  855. blocks' (block-handler/get-top-level-blocks blocks)
  856. mobile? (util/capacitor?)]
  857. (p/do!
  858. (when (and sibling-block (not mobile?))
  859. (let [{:keys [edit-block-f]} (move-to-prev-block repo sibling-block
  860. (get block :block/format :markdown)
  861. "")]
  862. (state/set-state! :editor/edit-block-fn edit-block-f)))
  863. (let [journals (and mobile? (filter ldb/journal? blocks'))
  864. blocks (remove (fn [b] (contains? (set (map :db/id journals)) (:db/id b))) blocks)]
  865. (when (or (seq journals) (seq blocks))
  866. (ui-outliner-tx/transact!
  867. {:outliner-op :delete-blocks
  868. :mobile-action-bar? mobile-action-bar?}
  869. (when (seq blocks)
  870. (outliner-op/delete-blocks! blocks nil))
  871. (when (seq journals)
  872. (doseq [journal journals]
  873. (outliner-op/delete-page! (:block/uuid journal)))))))))))
  874. (defn set-block-timestamp!
  875. [block-id key value]
  876. (let [key (string/lower-case (str key))
  877. block-id (if (string? block-id) (uuid block-id) block-id)
  878. value (str value)]
  879. (when-let [block (db/entity [:block/uuid block-id])]
  880. (let [{:block/keys [title]} block
  881. content (or title (state/get-edit-content))
  882. new-content (-> (text-util/remove-timestamp content key)
  883. (text-util/add-timestamp key value))]
  884. (when (not= content new-content)
  885. (let [input-id (state/get-edit-input-id)]
  886. (if (and input-id
  887. (string/ends-with? input-id (str block-id)))
  888. (state/set-edit-content! input-id new-content)
  889. (save-block-if-changed! block new-content))))))))
  890. (defn set-editing-block-timestamp!
  891. "Almost the same as set-block-timestamp! except for:
  892. - it doesn't save the block
  893. - it extracts current content from current input"
  894. [key value]
  895. (let [key (string/lower-case (str key))
  896. value (str value)
  897. content (state/get-edit-content)
  898. new-content (-> (text-util/remove-timestamp content key)
  899. (text-util/add-timestamp key value))]
  900. (when (not= content new-content)
  901. (let [input-id (state/get-edit-input-id)]
  902. (state/set-edit-content! input-id new-content)))))
  903. (defn set-blocks-id!
  904. "Persist block uuid to file if the uuid is valid, and it's not persisted in file.
  905. Accepts a list of uuids."
  906. [block-ids]
  907. (let [repo (state/get-current-repo)]
  908. (when-not (config/db-based-graph? repo)
  909. (file-editor-handler/set-blocks-id! block-ids))))
  910. (defn copy-block-ref!
  911. ([block-id]
  912. (copy-block-ref! block-id #(str %)))
  913. ([block-id tap-clipboard]
  914. (p/do!
  915. (save-current-block!)
  916. (set-blocks-id! [block-id])
  917. (util/copy-to-clipboard! (tap-clipboard block-id)))))
  918. (defn select-block!
  919. [block-uuid]
  920. (block-handler/select-block! block-uuid))
  921. (defn- compose-copied-blocks-contents
  922. [repo block-ids & {:as opts}]
  923. (let [blocks (map (fn [id] (db/entity [:block/uuid id])) block-ids)
  924. top-level-block-uuids (->> (block-handler/get-top-level-blocks blocks)
  925. (map :block/uuid))
  926. content (export-text/export-blocks-as-markdown
  927. repo top-level-block-uuids
  928. (merge
  929. opts
  930. {:indent-style (state/get-export-block-text-indent-style)
  931. :remove-options (set (state/get-export-block-text-remove-options))}))]
  932. [top-level-block-uuids content]))
  933. (defn- get-all-blocks-by-ids
  934. [repo ids]
  935. (loop [ids ids
  936. result []]
  937. (if (seq ids)
  938. (let [db-id (:db/id (db/entity [:block/uuid (first ids)]))
  939. blocks (tree/get-sorted-block-and-children repo db-id
  940. {:include-property-block? true})
  941. result (vec (concat result blocks))]
  942. (recur (remove (set (map :block/uuid result)) (rest ids)) result))
  943. result)))
  944. (defn copy-selection-blocks
  945. [html? & {:keys [selected-blocks] :as opts}]
  946. (let [repo (state/get-current-repo)
  947. selected-ids (state/get-selection-block-ids)
  948. ids (or (seq selected-ids) (map :block/uuid selected-blocks))
  949. [top-level-block-uuids content] (compose-copied-blocks-contents repo ids opts)
  950. block (db/entity [:block/uuid (first ids)])
  951. db-based? (config/db-based-graph? repo)]
  952. (when block
  953. (let [html (export-html/export-blocks-as-html repo top-level-block-uuids nil)
  954. copied-blocks (cond->> (get-all-blocks-by-ids repo top-level-block-uuids)
  955. db-based?
  956. (map (fn [block]
  957. (let [b (db/entity (:db/id block))]
  958. (->
  959. (->> (map (fn [[k v]]
  960. (let [v' (cond
  961. (and (map? v) (:db/id v))
  962. [:block/uuid (:block/uuid (db/entity (:db/id v)))]
  963. (and (coll? v) (every? #(and (map? %) (:db/id %)) v))
  964. (set (map (fn [i] [:block/uuid (:block/uuid (db/entity (:db/id i)))]) v))
  965. :else
  966. v)]
  967. [k v'])) b)
  968. (into {}))
  969. (assoc :db/id (:db/id b)))))))]
  970. (common-handler/copy-to-clipboard-without-id-property! repo (get block :block/format :markdown) content (when html? html) copied-blocks))
  971. (state/set-block-op-type! :copy)
  972. (when-not (util/capacitor?)
  973. (notification/show! "Copied!" :success)))))
  974. (defn copy-block-refs
  975. []
  976. (when-let [selected-blocks (seq (get-selected-blocks))]
  977. (let [blocks (->> (distinct (map #(when-let [id (dom/attr % "blockid")]
  978. (let [level (dom/attr % "level")]
  979. {:id (uuid id)
  980. :level (int level)}))
  981. selected-blocks))
  982. (remove nil?))
  983. first-block (first blocks)
  984. first-root-level-index (ffirst
  985. (filter (fn [[_ block]] (= (:level block) 1))
  986. (map-indexed vector blocks)))
  987. root-level (atom (:level first-block))
  988. adjusted-blocks (map-indexed
  989. (fn [index {:keys [id level]}]
  990. {:id id
  991. :level (if (< index first-root-level-index)
  992. (if (< level @root-level)
  993. (do
  994. (reset! root-level level)
  995. 1)
  996. (inc (- level @root-level)))
  997. level)})
  998. blocks)
  999. block (db/entity [:block/uuid (:id first-block)])
  1000. copy-str (some->> adjusted-blocks
  1001. (map (fn [{:keys [id level]}]
  1002. (if (config/db-based-graph? (state/get-current-repo))
  1003. (str (string/join (repeat (dec level) "\t")) "- " (ref/->page-ref id))
  1004. (condp = (get block :block/format :markdown)
  1005. :org
  1006. (str (string/join (repeat level "*")) " " (ref/->block-ref id))
  1007. :markdown
  1008. (str (string/join (repeat (dec level) "\t")) "- " (ref/->block-ref id))))))
  1009. (string/join "\n\n"))]
  1010. (set-blocks-id! (map :id blocks))
  1011. (util/copy-to-clipboard! copy-str))))
  1012. (defn copy-block-embeds
  1013. []
  1014. (when-let [blocks (seq (get-selected-blocks))]
  1015. (let [ids (->> (distinct (map #(when-let [id (dom/attr % "blockid")]
  1016. (uuid id)) blocks))
  1017. (remove nil?))
  1018. ids-str (if (config/db-based-graph? (state/get-current-repo))
  1019. (some->> ids
  1020. (map (fn [id] (ref/->block-ref id)))
  1021. (string/join "\n\n"))
  1022. (some->> ids
  1023. (map (fn [id] (util/format "{{embed ((%s))}}" id)))
  1024. (string/join "\n\n")))]
  1025. (set-blocks-id! ids)
  1026. (util/copy-to-clipboard! ids-str))))
  1027. (defn get-selected-toplevel-block-uuids
  1028. []
  1029. (when-let [blocks (seq (get-selected-blocks))]
  1030. (let [repo (state/get-current-repo)
  1031. block-ids (->> (distinct (map #(when-let [id (dom/attr % "blockid")]
  1032. (uuid id)) blocks))
  1033. (remove nil?))
  1034. blocks (db-utils/pull-many repo '[*] (mapv (fn [id] [:block/uuid id]) block-ids))
  1035. page-id (:db/id (:block/page (first blocks)))
  1036. ;; filter out blocks not belong to page with 'page-id'
  1037. blocks* (remove (fn [block] (some-> (:db/id (:block/page block)) (not= page-id))) blocks)]
  1038. (->> (block-handler/get-top-level-blocks blocks*)
  1039. (map :block/uuid)))))
  1040. (defn cut-selection-blocks
  1041. [copy? & {:keys [mobile-action-bar?]}]
  1042. (when copy? (copy-selection-blocks true))
  1043. (state/set-block-op-type! :cut)
  1044. (when-let [blocks (->> (get-selected-blocks)
  1045. (remove #(dom/has-class? % "property-value-container"))
  1046. (remove (fn [block] (or (= "true" (dom/attr block "data-query"))
  1047. (= "true" (dom/attr block "data-transclude")))))
  1048. seq)]
  1049. ;; remove queries
  1050. (let [dom-blocks (remove (fn [block] (= "true" (dom/attr block "data-query"))) blocks)]
  1051. (when (seq dom-blocks)
  1052. (let [repo (state/get-current-repo)
  1053. block-uuids (distinct (keep #(when-let [id (dom/attr % "blockid")] (uuid id)) dom-blocks))
  1054. lookup-refs (map (fn [id] [:block/uuid id]) block-uuids)
  1055. blocks (map db/entity lookup-refs)]
  1056. (ui-outliner-tx/transact!
  1057. {:outliner-op :delete-blocks}
  1058. (let [top-level-blocks (block-handler/get-top-level-blocks blocks)]
  1059. (when (seq top-level-blocks)
  1060. (let [sorted-blocks (mapcat (fn [block]
  1061. (tree/get-sorted-block-and-children repo (:db/id block)))
  1062. top-level-blocks)]
  1063. (when (seq sorted-blocks)
  1064. (delete-blocks! repo (map :block/uuid sorted-blocks) sorted-blocks dom-blocks mobile-action-bar?)))))))))))
  1065. (def url-regex
  1066. "Didn't use link/plain-link as it is incorrectly detects words as urls."
  1067. #"[^\s\(\[]+://[^\s\)\]]+")
  1068. (defn extract-nearest-link-from-text
  1069. [text pos & additional-patterns]
  1070. (let [;; didn't use page-ref regexs b/c it handles page-ref and org link cases
  1071. page-pattern #"\[\[([^\]]+)]]"
  1072. tag-pattern #"#\S+"
  1073. page-matches (util/re-pos page-pattern text)
  1074. block-matches (util/re-pos block-ref/block-ref-re text)
  1075. tag-matches (util/re-pos tag-pattern text)
  1076. additional-matches (mapcat #(util/re-pos % text) additional-patterns)
  1077. matches (->> (concat page-matches block-matches tag-matches additional-matches)
  1078. (remove nil?))
  1079. [_ match] (first (sort-by
  1080. (fn [[start-pos content]]
  1081. (let [end-pos (+ start-pos (count content))]
  1082. (cond
  1083. (< pos start-pos)
  1084. (- pos start-pos)
  1085. (> pos end-pos)
  1086. (- end-pos pos)
  1087. :else
  1088. 0)))
  1089. >
  1090. matches))]
  1091. (when match
  1092. (cond
  1093. (some #(re-find % match) additional-patterns)
  1094. match
  1095. (string/starts-with? match "#")
  1096. (subs match 1 (count match))
  1097. :else
  1098. (subs match 2 (- (count match) 2))))))
  1099. (defn- get-nearest-page-or-url
  1100. "Return the nearest page-name (not dereferenced, may be an alias), block, tag or url"
  1101. []
  1102. (when-let [block (state/get-edit-block)]
  1103. (when (:block/uuid block)
  1104. (when-let [edit-id (state/get-edit-input-id)]
  1105. (when-let [input (gdom/getElement edit-id)]
  1106. (when-let [pos (cursor/pos input)]
  1107. (let [value (gobj/get input "value")]
  1108. (extract-nearest-link-from-text value pos url-regex))))))))
  1109. (defn get-nearest-page
  1110. "Return the nearest page-name (not dereferenced, may be an alias), block or tag"
  1111. []
  1112. (when-let [block (state/get-edit-block)]
  1113. (when (:block/uuid block)
  1114. (when-let [edit-id (state/get-edit-input-id)]
  1115. (when-let [input (gdom/getElement edit-id)]
  1116. (when-let [pos (cursor/pos input)]
  1117. (let [value (gobj/get input "value")]
  1118. (extract-nearest-link-from-text value pos))))))))
  1119. (defn follow-link-under-cursor!
  1120. []
  1121. (when-let [page (get-nearest-page-or-url)]
  1122. (when-not (string/blank? page)
  1123. (p/do!
  1124. (state/clear-editor-action!)
  1125. (save-current-block!)
  1126. (if (re-find url-regex page)
  1127. (js/window.open page)
  1128. (do
  1129. (state/clear-edit!)
  1130. (route-handler/redirect-to-page! page)))))))
  1131. (defn open-link-in-sidebar!
  1132. []
  1133. (when-let [page (get-nearest-page)]
  1134. (let [page-name (string/lower-case page)
  1135. block? (util/uuid-string? page-name)]
  1136. (when-let [page (db/get-page page-name)]
  1137. (if block?
  1138. (state/sidebar-add-block!
  1139. (state/get-current-repo)
  1140. (:db/id page)
  1141. :block)
  1142. (state/sidebar-add-block!
  1143. (state/get-current-repo)
  1144. (:db/id page)
  1145. :page))))))
  1146. (declare save-current-block!)
  1147. ;; FIXME: shortcut `mod+.` doesn't work on Web (Chrome)
  1148. (defn zoom-in! []
  1149. (if (state/editing?)
  1150. (when-let [id (some-> (state/get-edit-block)
  1151. :block/uuid
  1152. ((fn [id] [:block/uuid id]))
  1153. db/entity
  1154. :block/uuid)]
  1155. (state/clear-editor-action!)
  1156. (state/set-editing-block-id! [:unknown-container id])
  1157. (p/do!
  1158. (save-current-block!)
  1159. (route-handler/redirect-to-page! id)))
  1160. (js/window.history.forward)))
  1161. (defn zoom-out!
  1162. []
  1163. (if (state/editing?)
  1164. (let [page (state/get-current-page)
  1165. block-id (and (string? page) (parse-uuid page))]
  1166. (p/do!
  1167. (state/clear-editor-action!)
  1168. (save-current-block!)
  1169. (when block-id
  1170. (state/set-editing-block-id! [:unknown-container (:block/uuid (state/get-edit-block))])
  1171. (let [block-parent (db/get-block-parent block-id)]
  1172. (if-let [id (and
  1173. (nil? (:block/name block-parent))
  1174. (:block/uuid block-parent))]
  1175. (route-handler/redirect-to-page! id)
  1176. (let [page-id (some-> (db/entity [:block/uuid block-id])
  1177. :block/page
  1178. :db/id)]
  1179. (when-let [page (db/entity page-id)]
  1180. (route-handler/redirect-to-page! (:block/uuid page)))))))))
  1181. (js/window.history.back)))
  1182. (defn cut-block!
  1183. [block-id]
  1184. (when-let [block (db/entity [:block/uuid block-id])]
  1185. (let [repo (state/get-current-repo)
  1186. ;; TODO: support org mode
  1187. [_top-level-block-uuids md-content] (compose-copied-blocks-contents repo [block-id])
  1188. html (export-html/export-blocks-as-html repo [block-id] nil)
  1189. sorted-blocks (tree/get-sorted-block-and-children repo (:db/id block))]
  1190. (common-handler/copy-to-clipboard-without-id-property! repo (get block :block/format :markdown) md-content html sorted-blocks)
  1191. (state/set-block-op-type! :cut)
  1192. (delete-block-aux! block))))
  1193. (defn highlight-selection-area!
  1194. [end-block-id block-dom-element & {:keys [append?]}]
  1195. (when-let [start-node (state/get-selection-start-block-or-first)]
  1196. (let [end-block-node block-dom-element
  1197. select-direction (state/get-selection-direction)
  1198. selected-blocks (state/get-unsorted-selection-blocks)
  1199. last-node (last selected-blocks)
  1200. latest-visible-block (or last-node start-node)
  1201. latest-block-id (when latest-visible-block (.-id latest-visible-block))]
  1202. (if (and start-node end-block-node)
  1203. (let [blocks (util/get-nodes-between-two-nodes start-node end-block-node "ls-block")
  1204. direction (util/get-direction-between-two-nodes start-node end-block-node "ls-block")
  1205. blocks (if (= direction :up) (reverse blocks) blocks)]
  1206. (state/exit-editing-and-set-selected-blocks! blocks direction))
  1207. (when latest-visible-block
  1208. (let [blocks (util/get-nodes-between-two-nodes latest-visible-block end-block-node "ls-block")
  1209. direction (if (= latest-block-id end-block-id)
  1210. select-direction
  1211. (util/get-direction-between-two-nodes latest-visible-block end-block-node "ls-block"))
  1212. blocks (if (= direction :up) (reverse (util/sort-by-height blocks)) (util/sort-by-height blocks))]
  1213. (if append?
  1214. (do (state/clear-edit!)
  1215. (if (and select-direction (not= direction select-direction))
  1216. (state/drop-selection-blocks-starts-with! end-block-node)
  1217. (state/conj-selection-block! blocks direction)))
  1218. (state/exit-editing-and-set-selected-blocks! blocks direction))))))))
  1219. (defonce *action-bar-timeout (atom nil))
  1220. (defn popup-exists?
  1221. [id]
  1222. (some->> (shui-popup/get-popups)
  1223. (some #(some-> % (:id) (str) (string/includes? (str id))))))
  1224. (defn dialog-exists?
  1225. [id]
  1226. (shui-dialog/get-modal id))
  1227. (defn show-action-bar!
  1228. [& {:keys [delay]
  1229. :or {delay 200}}]
  1230. (when (and (config/db-based-graph?) (not (popup-exists? :selection-action-bar)))
  1231. (when-let [timeout @*action-bar-timeout]
  1232. (js/clearTimeout timeout))
  1233. (state/pub-event! [:editor/hide-action-bar])
  1234. (when (seq (remove (fn [b] (dom/has-class? b "ls-table-cell"))
  1235. (state/get-selection-blocks)))
  1236. (let [timeout (js/setTimeout #(state/pub-event! [:editor/show-action-bar]) delay)]
  1237. (reset! *action-bar-timeout timeout)))))
  1238. (defn- select-block-up-down
  1239. [direction]
  1240. (cond
  1241. ;; when editing, quit editing and select current block
  1242. (state/editing?)
  1243. (when-let [element (state/get-editor-block-container)]
  1244. (when element
  1245. (p/do!
  1246. (save-current-block!)
  1247. (util/scroll-to-block element)
  1248. (state/exit-editing-and-set-selected-blocks! [element]))))
  1249. ;; when selection and one block selected, select next block
  1250. (and (state/selection?) (== 1 (count (state/get-selection-blocks))))
  1251. (let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
  1252. element (f (first (state/get-selection-blocks))
  1253. {:up-down? true
  1254. :exclude-property? true})]
  1255. (when element
  1256. (util/scroll-to-block element)
  1257. (state/conj-selection-block! element direction)))
  1258. ;; if same direction, keep conj on same direction
  1259. (and (state/selection?) (= direction (state/get-selection-direction)))
  1260. (let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
  1261. first-last (if (= :up direction) first last)
  1262. element (f (first-last (state/get-selection-blocks))
  1263. {:up-down? true
  1264. :exclude-property? true})]
  1265. (when element
  1266. (util/scroll-to-block element)
  1267. (state/conj-selection-block! element direction)))
  1268. ;; if different direction, keep clear until one left
  1269. (state/selection?)
  1270. (let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed)
  1271. last-first (if (= :up direction) last first)
  1272. element (f (last-first (state/get-selection-blocks))
  1273. {:up-down? true
  1274. :exclude-property? true})]
  1275. (when element
  1276. (util/scroll-to-block element)
  1277. (state/drop-last-selection-block!))))
  1278. (show-action-bar! {:delay 500})
  1279. nil)
  1280. (defn on-select-block
  1281. [direction]
  1282. (fn [_event]
  1283. (select-block-up-down direction)))
  1284. (defn save-block-aux!
  1285. [block value opts]
  1286. (let [entity (db/entity [:block/uuid (:block/uuid block)])]
  1287. (when (and (:db/id entity) (not (ldb/built-in? entity)))
  1288. (let [value (string/trim value)]
  1289. ;; FIXME: somehow frontend.components.editor's will-unmount event will loop forever
  1290. ;; maybe we shouldn't save the block/file in "will-unmount" event?
  1291. (save-block-if-changed! block value opts)))))
  1292. (defn save-block!
  1293. ([repo block-or-uuid content]
  1294. (save-block! repo block-or-uuid content {}))
  1295. ([repo block-or-uuid content {:keys [properties] :as opts}]
  1296. (let [block (if (or (uuid? block-or-uuid)
  1297. (string? block-or-uuid))
  1298. (db-model/query-block-by-uuid block-or-uuid) block-or-uuid)]
  1299. (save-block!
  1300. {:block block :repo repo :opts (dissoc opts :properties)}
  1301. (if (seq properties)
  1302. (property-file/insert-properties-when-file-based repo (get block :block/format :markdown) content properties)
  1303. content))))
  1304. ([{:keys [block repo opts] :as _state} value]
  1305. (let [repo (or repo (state/get-current-repo))]
  1306. (when (db/entity repo [:block/uuid (:block/uuid block)])
  1307. (save-block-aux! block value opts)))))
  1308. (defn save-blocks!
  1309. [blocks]
  1310. (ui-outliner-tx/transact!
  1311. {:outliner-op :save-block}
  1312. (doseq [[block value] blocks]
  1313. (save-block-if-changed! block value))))
  1314. (defonce *auto-save-timeout (atom nil))
  1315. (defn- clear-block-auto-save-timeout!
  1316. []
  1317. (when @*auto-save-timeout
  1318. (js/clearTimeout @*auto-save-timeout)))
  1319. (defn save-current-block!
  1320. "skip-properties? if set true, when editing block is likely be properties, skip saving"
  1321. ([]
  1322. (save-current-block! {}))
  1323. ([{:keys [force? skip-properties? current-block] :as opts}]
  1324. (clear-block-auto-save-timeout!)
  1325. ;; non English input method
  1326. (when-not (or (state/editor-in-composition?)
  1327. (state/get-editor-action))
  1328. (when (state/get-current-repo)
  1329. (try
  1330. (let [input-id (state/get-edit-input-id)
  1331. block (state/get-edit-block)
  1332. db-block (when-let [block-id (:block/uuid block)]
  1333. (db/entity [:block/uuid block-id]))
  1334. elem (and input-id (gdom/getElement input-id))
  1335. db-content (:block/title db-block)
  1336. db-content-without-heading (and db-content
  1337. (common-util/safe-subs db-content (:block/level db-block)))
  1338. value (if (= (:block/uuid current-block) (:block/uuid block))
  1339. (:block/title current-block)
  1340. (and elem (gobj/get elem "value")))]
  1341. (when value
  1342. (cond
  1343. force?
  1344. (save-block-aux! db-block value opts)
  1345. (and skip-properties?
  1346. (db-model/top-block? block)
  1347. (when elem (thingatpt/properties-at-point elem)))
  1348. nil
  1349. (and block value db-content-without-heading
  1350. (not= (string/trim db-content-without-heading)
  1351. (string/trim value)))
  1352. (save-block-aux! db-block value opts))))
  1353. (catch :default error
  1354. (log/error :save-block-failed error)))))))
  1355. (defn- clean-content!
  1356. [repo format content]
  1357. (if (config/db-based-graph? repo)
  1358. content
  1359. (some->> (text/remove-level-spaces content format (config/get-block-pattern format))
  1360. (drawer/remove-logbook)
  1361. (property-file/remove-properties-when-file-based repo format)
  1362. string/trim)))
  1363. (defn delete-asset-of-block!
  1364. [{:keys [repo asset-block href full-text block-id local? delete-local?] :as _opts}]
  1365. (let [block (db-model/query-block-by-uuid block-id)
  1366. _ (or block (throw (ex-info (str block-id " not exists")
  1367. {:block-id block-id})))
  1368. text (:block/title block)
  1369. content (if asset-block
  1370. (string/replace text (ref/->page-ref (:block/uuid asset-block)) "")
  1371. (string/replace text full-text ""))]
  1372. (save-block! repo block content)
  1373. (when (and local? delete-local?)
  1374. (if asset-block
  1375. (delete-block-aux! asset-block)
  1376. (when-let [href (if (util/electron?) href
  1377. (second (re-find #"\((.+)\)$" full-text)))]
  1378. (let [block-file-rpath (file-model/get-block-file-path block)
  1379. asset-fpath (if (string/starts-with? href "assets://")
  1380. (path/url-to-path href)
  1381. (config/get-repo-fpath
  1382. repo
  1383. (path/resolve-relative-path block-file-rpath href)))]
  1384. (fs/unlink! repo asset-fpath nil)))))))
  1385. (defn db-based-save-asset!
  1386. [repo dir file file-rpath]
  1387. (p/let [buffer (.arrayBuffer file)]
  1388. (if (util/electron?)
  1389. (ipc/ipc "writeFile" repo (path/path-join dir file-rpath) buffer)
  1390. ;; web
  1391. (p/let [buffer (.arrayBuffer file)
  1392. content (js/Uint8Array. buffer)]
  1393. ;; actually, writing binary using memory fs
  1394. (fs/write-plain-text-file! repo dir file-rpath content nil)))))
  1395. (defn- new-asset-block
  1396. [repo ^js file repo-dir asset-dir-rpath]
  1397. ;; WARN file name maybe fully qualified path when paste file
  1398. (p/let [file-name (node-path/basename (.-name file))
  1399. file-name-without-ext* (db-asset/asset-name->title file-name)
  1400. file-name-without-ext (if (= file-name-without-ext* "image")
  1401. (date/get-date-time-string-2)
  1402. file-name-without-ext*)
  1403. checksum (assets-handler/get-file-checksum file)
  1404. existing-asset (db-async/<get-asset-with-checksum repo checksum)]
  1405. (if existing-asset
  1406. (do
  1407. (notification/show! (str "Asset exists already, title: " (:block/title existing-asset)
  1408. ", node reference: [[" (:block/uuid existing-asset) "]]")
  1409. :warning
  1410. false)
  1411. nil)
  1412. (p/let [block-id (ldb/new-block-id)
  1413. ext (when file-name (db-asset/asset-path->type file-name))
  1414. _ (when (string/blank? ext)
  1415. (throw (ex-info "File doesn't have a valid ext."
  1416. {:file-name file-name})))
  1417. file-path (str block-id "." ext)
  1418. file-rpath (str asset-dir-rpath "/" file-path)
  1419. dir repo-dir
  1420. asset (db/entity :logseq.class/Asset)]
  1421. (if (assets-handler/exceed-limit-size? file)
  1422. (do
  1423. (notification/show! [:div "Asset size shouldn't be larger than 100M"]
  1424. :warning
  1425. false)
  1426. (throw (ex-info "Asset size shouldn't be larger than 100M" {:file-name file-name})))
  1427. (p/do!
  1428. (db-based-save-asset! repo dir file file-rpath)
  1429. {:block/title file-name-without-ext
  1430. :block/uuid block-id
  1431. :logseq.property.asset/type ext
  1432. :logseq.property.asset/size (.-size file)
  1433. :logseq.property.asset/checksum checksum
  1434. :block/tags #{(:db/id asset)}}))))))
  1435. (defn db-based-save-assets!
  1436. "Save incoming(pasted) assets to assets directory.
  1437. Returns: asset entities"
  1438. [repo files & {:keys [pdf-area? last-edit-block]}]
  1439. (p/let [[repo-dir asset-dir-rpath] (assets-handler/ensure-assets-dir! repo)
  1440. today-page-name (date/today)
  1441. today-page-e (db-model/get-journal-page today-page-name)
  1442. today-page (if (nil? today-page-e)
  1443. (state/pub-event! [:page/create today-page-name])
  1444. today-page-e)
  1445. blocks* (p/all
  1446. (for [^js file files]
  1447. (new-asset-block repo file repo-dir asset-dir-rpath)))
  1448. blocks (remove nil? blocks*)
  1449. edit-block (or (state/get-edit-block) last-edit-block)
  1450. insert-to-current-block-page? (and (:block/uuid edit-block) (not pdf-area?))
  1451. target (if insert-to-current-block-page?
  1452. edit-block
  1453. today-page)]
  1454. (when-not target
  1455. (throw (ex-info "invalid target" {:files files
  1456. :today-page today-page
  1457. :edit-block edit-block})))
  1458. (when (seq blocks)
  1459. (p/do!
  1460. (ui-outliner-tx/transact!
  1461. {:outliner-op :insert-blocks}
  1462. (outliner-op/insert-blocks! blocks target {:keep-uuid? true
  1463. :sibling? (= edit-block target)
  1464. :replace-empty-target? true}))
  1465. (map (fn [b] (db/entity [:block/uuid (:block/uuid b)])) blocks)))))
  1466. (def insert-command! editor-common-handler/insert-command!)
  1467. (defn db-upload-assets!
  1468. "Paste asset for db graph and insert link to current editing block"
  1469. [repo id ^js files format uploading? drop-or-paste?]
  1470. (when (config/db-based-graph? repo)
  1471. (insert-command!
  1472. id
  1473. ""
  1474. format
  1475. {:last-pattern (if drop-or-paste? "" commands/command-trigger)
  1476. :restore? true
  1477. :command :insert-asset})
  1478. (-> (db-based-save-assets! repo (js->clj files))
  1479. (p/catch (fn [e]
  1480. (js/console.error e)))
  1481. (p/finally
  1482. (fn []
  1483. (reset! uploading? false)
  1484. (reset! *asset-uploading? false)
  1485. (reset! *asset-uploading-process 0))))))
  1486. (defn upload-asset!
  1487. "Paste asset and insert link to current editing block"
  1488. [id ^js files format uploading? drop-or-paste?]
  1489. (let [repo (state/get-current-repo)]
  1490. (if (config/db-based-graph? repo)
  1491. (db-upload-assets! repo id ^js files format uploading? drop-or-paste?)
  1492. (file-editor-handler/file-upload-assets! repo id ^js files format uploading? *asset-uploading? *asset-uploading-process drop-or-paste?))))
  1493. ;; Editor should track some useful information, like editor modes.
  1494. ;; For example:
  1495. ;; 1. Which file format is it, markdown or org mode?
  1496. ;; 2. Is it in the properties area? Then we can enable the ":" autopair
  1497. (def autopair-map
  1498. {"[" "]"
  1499. "{" "}"
  1500. "(" ")"
  1501. "`" "`"
  1502. "~" "~"
  1503. "*" "*"
  1504. "_" "_"
  1505. "^" "^"
  1506. "=" "="
  1507. "/" "/"
  1508. "+" "+"})
  1509. ;; ":" ":" ; TODO: only properties editing and org mode tag
  1510. (def reversed-autopair-map
  1511. (zipmap (vals autopair-map)
  1512. (keys autopair-map)))
  1513. (def autopair-when-selected
  1514. #{"*" "^" "_" "=" "+" "/"})
  1515. (def delete-map
  1516. (assoc autopair-map
  1517. "$" "$"
  1518. ":" ":"))
  1519. (defn- autopair
  1520. [input-id prefix _format _option]
  1521. (let [value (get autopair-map prefix)
  1522. selected (util/get-selected-text)
  1523. postfix (str selected value)
  1524. value (str prefix postfix)
  1525. input (gdom/getElement input-id)]
  1526. (when value
  1527. (let [[prefix _pos] (commands/simple-replace! input-id value selected
  1528. {:backward-pos (count postfix)
  1529. :check-fn (fn [new-value prefix-pos]
  1530. (when (>= prefix-pos 0)
  1531. [(subs new-value prefix-pos (+ prefix-pos 2))
  1532. (+ prefix-pos 2)]))})]
  1533. (cond
  1534. (= prefix page-ref/left-brackets)
  1535. (do
  1536. (commands/handle-step [:editor/search-page])
  1537. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)
  1538. :selected selected}))
  1539. (and (= prefix block-ref/left-parens)
  1540. (config/db-based-graph? (state/get-current-repo)))
  1541. (notification/show!
  1542. "To reference a node, please use `[[]]`."
  1543. :warning)
  1544. (= prefix block-ref/left-parens)
  1545. (do
  1546. (commands/handle-step [:editor/search-block :reference])
  1547. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)
  1548. :selected selected})))))))
  1549. (defn surround-by?
  1550. [input before end]
  1551. (when input
  1552. (let [value (gobj/get input "value")
  1553. pos (cursor/pos input)]
  1554. (text-util/surround-by? value pos before end))))
  1555. (defn- autopair-left-paren?
  1556. [input key]
  1557. (and (= key "(")
  1558. (or (surround-by? input :start "")
  1559. (surround-by? input "\n" "")
  1560. (surround-by? input " " "")
  1561. (surround-by? input "]" "")
  1562. (surround-by? input "(" ""))))
  1563. (defn wrapped-by?
  1564. [input before end]
  1565. (when input
  1566. (let [value (gobj/get input "value")
  1567. pos (cursor/pos input)]
  1568. (when (>= pos 0)
  1569. (text-util/wrapped-by? value pos before end)))))
  1570. (defn get-matched-classes
  1571. "Return matched classes except the root tag"
  1572. [q]
  1573. (let [editing-block (some-> (state/get-edit-block) :db/id db/entity)
  1574. non-page-block? (and editing-block (not (ldb/page? editing-block)))
  1575. all-classes (cond-> (db-model/get-all-classes (state/get-current-repo) {:except-root-class? true})
  1576. non-page-block?
  1577. (conj (db/entity :logseq.class/Page)))
  1578. classes (->> all-classes
  1579. (mapcat (fn [class]
  1580. (conj (:block/alias class) class)))
  1581. (common-util/distinct-by :db/id)
  1582. (map (fn [e] (select-keys e [:block/uuid :block/title]))))]
  1583. (search/fuzzy-search classes q {:extract-fn :block/title})))
  1584. (defn <get-matched-blocks
  1585. "Return matched blocks that are not built-in"
  1586. [q & [{:keys [nlp-pages? page-only?]}]]
  1587. (p/let [block (state/get-edit-block)
  1588. result (search/block-search (state/get-current-repo) q {:built-in? false
  1589. :enable-snippet? false
  1590. :page-only? page-only?})
  1591. matched (remove (fn [b] (= (:block/uuid b) (:block/uuid block))) result)
  1592. result' (-> (concat matched
  1593. (when nlp-pages?
  1594. (map (fn [title] {:block/title title :nlp-date? true :page? true})
  1595. date/nlp-pages)))
  1596. (search/fuzzy-search q {:extract-fn :block/title :limit 50}))
  1597. result'' (let [ids (set (map :block/uuid result'))]
  1598. (concat result' (remove (fn [item] (ids (:block/uuid item))) matched)))]
  1599. (sort-by (complement :page?) result'')))
  1600. (defn <get-matched-templates
  1601. [q]
  1602. (search/template-search q))
  1603. (defn <get-matched-properties
  1604. [q]
  1605. (search/property-search q))
  1606. (defn get-matched-property-values
  1607. [property q]
  1608. (search/property-value-search property q))
  1609. (defn get-last-command
  1610. [input]
  1611. (try
  1612. (let [edit-content (or (gobj/get input "value") "")
  1613. pos (cursor/pos input)
  1614. last-slash-caret-pos (:pos (:pos (state/get-editor-action-data)))
  1615. last-command (and last-slash-caret-pos (subs edit-content last-slash-caret-pos pos))]
  1616. (when (> pos 0) last-command))
  1617. (catch :default e
  1618. (js/console.error e)
  1619. nil)))
  1620. (defn get-matched-commands
  1621. [command]
  1622. (condp = command
  1623. nil nil
  1624. "" @commands/*initial-commands
  1625. (commands/get-matched-commands command)))
  1626. (defn auto-complete?
  1627. []
  1628. (or @*asset-uploading?
  1629. (state/get-editor-action)))
  1630. (defn in-shui-popup?
  1631. []
  1632. (or (some-> js/document.activeElement
  1633. (.closest "[data-radix-menu-content]")
  1634. (nil?)
  1635. (not))
  1636. (.querySelector js/document.body
  1637. "div[data-radix-popper-content-wrapper]")))
  1638. (defn get-current-input-char
  1639. [input]
  1640. (when-let [pos (cursor/pos input)]
  1641. (let [value (gobj/get input "value")]
  1642. (when (and (>= (count value) (inc pos))
  1643. (>= pos 1))
  1644. (util/nth-safe value pos)))))
  1645. (defn move-up-down
  1646. [up?]
  1647. (fn [event]
  1648. (util/stop event)
  1649. (state/pub-event! [:editor/hide-action-bar])
  1650. (let [edit-block-id (:block/uuid (state/get-edit-block))
  1651. move-nodes (fn [blocks]
  1652. (let [blocks' (block-handler/get-top-level-blocks blocks)
  1653. result (ui-outliner-tx/transact!
  1654. {:outliner-op :move-blocks}
  1655. (outliner-op/move-blocks-up-down! blocks' up?))]
  1656. (when-let [block-node (util/get-first-block-by-id (:block/uuid (first blocks)))]
  1657. (.scrollIntoView block-node #js {:behavior "smooth" :block "nearest"}))
  1658. result))]
  1659. (if edit-block-id
  1660. (when-let [block (db/entity [:block/uuid edit-block-id])]
  1661. (let [blocks [(assoc block :block/title (state/get-edit-content))]
  1662. container-id (get-new-container-id (if up? :move-up :move-down) {})]
  1663. (p/do!
  1664. (save-current-block!)
  1665. (move-nodes blocks)
  1666. (if container-id
  1667. (state/set-editing-block-id! [container-id edit-block-id])
  1668. (when-let [input (some-> (state/get-edit-input-id) gdom/getElement)]
  1669. (.focus input)
  1670. (util/scroll-editor-cursor input))))))
  1671. (let [ids (state/get-selection-block-ids)]
  1672. (when (seq ids)
  1673. (let [lookup-refs (map (fn [id] [:block/uuid id]) ids)
  1674. blocks (map db/entity lookup-refs)]
  1675. (move-nodes blocks))))))))
  1676. (defn get-selected-ordered-blocks
  1677. []
  1678. (let [repo (state/get-current-repo)
  1679. ids (state/get-selection-block-ids)
  1680. lookup-refs (->> (map (fn [id] [:block/uuid id]) ids)
  1681. (remove nil?))]
  1682. (db/pull-many repo '[*] lookup-refs)))
  1683. (defn on-tab
  1684. "`direction` = :left | :right."
  1685. [direction]
  1686. (let [blocks (get-selected-ordered-blocks)]
  1687. (block-handler/indent-outdent-blocks! blocks (= direction :right) nil)))
  1688. (defn- get-link [format link label]
  1689. (let [link (or link "")
  1690. label (or label "")]
  1691. (case (keyword format)
  1692. :markdown (util/format "[%s](%s)" label link)
  1693. :org (util/format "[[%s][%s]]" link label)
  1694. nil)))
  1695. (defn- get-image-link
  1696. [format link label]
  1697. (let [link (or link "")
  1698. label (or label "")]
  1699. (case (keyword format)
  1700. :markdown (util/format "![%s](%s)" label link)
  1701. :org (util/format "[[%s]]"))))
  1702. (defn handle-command-input-close [id]
  1703. (state/set-editor-show-input! nil)
  1704. (when-let [saved-cursor (state/get-editor-last-pos)]
  1705. (when-let [input (gdom/getElement id)]
  1706. (cursor/move-cursor-to input saved-cursor true))))
  1707. (defn handle-command-input [command id format m]
  1708. ;; TODO: Add error handling for when user doesn't provide a required field.
  1709. ;; (The current behavior is to just revert back to the editor.)
  1710. (case command
  1711. :link (let [{:keys [link label]} m]
  1712. (when-not (or (string/blank? link) (string/blank? label))
  1713. (insert-command!
  1714. id
  1715. (get-link format link label)
  1716. format
  1717. {:last-pattern (str commands/command-trigger "link")
  1718. :command :link})))
  1719. :image-link (let [{:keys [link label]} m]
  1720. (when (not (string/blank? link))
  1721. (insert-command!
  1722. id
  1723. (get-image-link format link label)
  1724. format
  1725. {:last-pattern (str commands/command-trigger "link")
  1726. :command :image-link})))
  1727. nil)
  1728. (handle-command-input-close id))
  1729. (defn restore-last-saved-cursor!
  1730. ([] (restore-last-saved-cursor! (state/get-input)))
  1731. ([input]
  1732. (when-let [saved-cursor (and input (state/get-editor-last-pos))]
  1733. (cursor/move-cursor-to input saved-cursor true))))
  1734. (defn- close-autocomplete-if-outside
  1735. [input]
  1736. (when (and input
  1737. (contains? #{:page-search :page-search-hashtag :block-search} (state/get-editor-action))
  1738. (not (wrapped-by? input page-ref/left-brackets page-ref/right-brackets))
  1739. (not (wrapped-by? input block-ref/left-parens block-ref/right-parens))
  1740. ;; wrapped-by? doesn't detect multiple beginnings when ending with "" so
  1741. ;; use subs to correctly detect current hashtag
  1742. (not (text-util/wrapped-by? (subs (.-value input) 0 (cursor/pos input)) (cursor/pos input) commands/hashtag ""))
  1743. (not (and (config/db-based-graph? (state/get-current-repo))
  1744. (= :block-search (state/get-editor-action)))))
  1745. (state/clear-editor-action!)))
  1746. (defn resize-image!
  1747. [config block-id metadata full_text size]
  1748. (let [asset (:asset-block config)]
  1749. (if (config/db-based-graph?)
  1750. (property-handler/set-block-property! (state/get-current-repo)
  1751. (if asset (:db/id asset) block-id)
  1752. :logseq.property.asset/resize-metadata
  1753. size)
  1754. (let [new-meta (merge metadata size)
  1755. image-part (first (string/split full_text #"\{"))
  1756. md-link? (string/starts-with? image-part "![")
  1757. new-full-text (str (if md-link? image-part (str "![image](" image-part ")")) (pr-str new-meta))
  1758. block (db/entity [:block/uuid block-id])
  1759. value (:block/title block)
  1760. new-value (string/replace value full_text new-full-text)]
  1761. (save-block-aux! block new-value {})))))
  1762. (defn edit-box-on-change!
  1763. [e block id]
  1764. (when (= (:db/id block) (:db/id (state/get-edit-block)))
  1765. (let [value (util/evalue e)
  1766. repo (state/get-current-repo)]
  1767. (state/set-edit-content! id value false)
  1768. (clear-block-auto-save-timeout!)
  1769. (block-handler/mark-last-input-time! repo)
  1770. (reset! *auto-save-timeout
  1771. (js/setTimeout
  1772. (fn []
  1773. (when (and (state/input-idle? repo :diff 450)
  1774. ;; don't auto-save block if it has tags
  1775. (not (and
  1776. (config/db-based-graph? repo)
  1777. (re-find #"#\S+" value))))
  1778. ; don't auto-save for page's properties block
  1779. (save-current-block! {:skip-properties? true})))
  1780. 450)))))
  1781. (defn- start-of-new-word?
  1782. [input pos]
  1783. (contains? #{" " "\t"} (get (.-value input) (- pos 2))))
  1784. (defn handle-last-input []
  1785. (let [input (state/get-input)
  1786. input-id (state/get-edit-input-id)
  1787. edit-block (state/get-edit-block)
  1788. pos (cursor/pos input)
  1789. content (.-value input)
  1790. last-input-char (util/nth-safe content (dec pos))
  1791. last-prev-input-char (util/nth-safe content (dec (dec pos)))
  1792. prev-prev-input-char (util/nth-safe content (- pos 3))
  1793. repo (state/get-current-repo)
  1794. db-based? (config/db-based-graph? repo)]
  1795. ;; TODO: is it cross-browser compatible?
  1796. ;; (not= (gobj/get native-e "inputType") "insertFromPaste")
  1797. (cond
  1798. (and (= content "1. ") (= last-input-char " ") input-id edit-block
  1799. (not (own-order-number-list? edit-block)))
  1800. (p/let [_ (state/pub-event! [:editor/toggle-own-number-list edit-block])]
  1801. (state/set-edit-content! input-id ""))
  1802. (and (= last-input-char commands/command-trigger)
  1803. (or (re-find #"(?m)^/" (str (.-value input))) (start-of-new-word? input pos)))
  1804. (do
  1805. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
  1806. (commands/reinit-matched-commands!)
  1807. (state/set-editor-show-commands!))
  1808. (and (= last-input-char last-prev-input-char commands/colon)
  1809. (or (nil? prev-prev-input-char)
  1810. (= prev-prev-input-char "\n"))
  1811. (not db-based?))
  1812. (do
  1813. (cursor/move-cursor-backward input 2)
  1814. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
  1815. (state/set-editor-action! :property-search))
  1816. (and
  1817. (not= :property-search (state/get-editor-action))
  1818. (let [{:keys [line start-pos]} (text-util/get-current-line-by-pos (.-value input) (dec pos))]
  1819. (text-util/wrapped-by? line (- pos start-pos) "" gp-property/colons))
  1820. (not db-based?))
  1821. (do
  1822. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
  1823. (state/set-editor-action! :property-search))
  1824. (and (= last-input-char commands/colon)
  1825. (= :property-search (state/get-editor-action))
  1826. (not db-based?))
  1827. (state/clear-editor-action!)
  1828. (or (= last-input-char last-prev-input-char commands/hashtag)
  1829. (and (= last-prev-input-char commands/hashtag)
  1830. (= last-input-char " ")))
  1831. (state/clear-editor-action!)
  1832. ;; Open "Search page or New page" auto-complete
  1833. (and (= last-input-char commands/hashtag)
  1834. ;; Only trigger at beginning of a line, before whitespace or after a reference
  1835. (or (re-find #"(?m)^#" (str (.-value input)))
  1836. (start-of-new-word? input pos)
  1837. (and db-based? (= page-ref/right-brackets (common-util/safe-subs (str (.-value input)) (- pos 3) (dec pos))))))
  1838. (do
  1839. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
  1840. (state/set-editor-last-pos! pos)
  1841. (state/set-editor-action! :page-search-hashtag))
  1842. :else
  1843. nil)))
  1844. (defn get-selected-text
  1845. []
  1846. (let [text (:selected (state/get-editor-action-data))]
  1847. (when-not (string/blank? text)
  1848. text)))
  1849. (defn block-on-chosen-handler
  1850. [id q format selected-text]
  1851. (fn [chosen _click?]
  1852. (state/clear-editor-action!)
  1853. (let [uuid-string (str (:block/uuid chosen))]
  1854. ;; block reference
  1855. (insert-command! id
  1856. (ref/->block-ref uuid-string)
  1857. format
  1858. {:last-pattern (str block-ref/left-parens (if selected-text "" q))
  1859. :end-pattern block-ref/right-parens
  1860. :postfix-fn (fn [s] (util/replace-first block-ref/right-parens s ""))
  1861. :forward-pos 3
  1862. :command :block-ref})
  1863. ;; Save it so it'll be parsed correctly in the future
  1864. (property-handler/file-persist-block-id! (state/get-current-repo) (:block/uuid chosen))
  1865. (when-let [input (gdom/getElement id)]
  1866. (.focus input)))))
  1867. (defn block-non-exist-handler
  1868. [input]
  1869. (fn []
  1870. (state/clear-editor-action!)
  1871. (cursor/move-cursor-forward input 2)))
  1872. (defn- paste-block-cleanup
  1873. [repo block page exclude-properties format content-update-fn keep-uuid?]
  1874. (let [db-based? (config/db-based-graph? (state/get-current-repo))
  1875. new-content
  1876. (if content-update-fn
  1877. (content-update-fn (:block/title block))
  1878. (:block/title block))
  1879. new-content
  1880. (cond->> new-content
  1881. (not keep-uuid?) (property-file/remove-property-when-file-based repo format "id")
  1882. true (property-file/remove-property-when-file-based repo format "custom_id"))]
  1883. (merge (apply dissoc block (conj (if-not keep-uuid? [:block/_refs] []) :block/pre-block? :block/meta))
  1884. (cond->
  1885. {:block/page {:db/id (:db/id page)}
  1886. :block/title new-content}
  1887. (not db-based?)
  1888. (assoc :block/properties (apply dissoc (not-empty (:block/properties block))
  1889. (concat
  1890. (when-not keep-uuid? [:id])
  1891. [:custom_id :custom-id]
  1892. exclude-properties))
  1893. :block/properties-text-values (apply dissoc (:block/properties-text-values block)
  1894. (concat
  1895. (when-not keep-uuid? [:id])
  1896. exclude-properties))
  1897. :block/format format)))))
  1898. (defn- edit-last-block-after-inserted!
  1899. [result]
  1900. (util/schedule
  1901. (fn []
  1902. (when-let [last-block (last (:blocks result))]
  1903. (clear-when-saved!)
  1904. (let [last-block' (db/entity [:block/uuid (:block/uuid last-block)])]
  1905. (edit-block! last-block' :max))))))
  1906. (defn- nested-blocks
  1907. [blocks]
  1908. (let [ids (set (map :db/id blocks))]
  1909. (some? (some #(ids (:db/id (:block/parent %))) blocks))))
  1910. (defn paste-blocks
  1911. "Given a vec of blocks, insert them into the target page.
  1912. keep-uuid?: if true, keep the uuid provided in the block structure."
  1913. [blocks {:keys [content-update-fn
  1914. exclude-properties
  1915. target-block
  1916. sibling?
  1917. keep-uuid?
  1918. revert-cut-txs
  1919. skip-empty-target?
  1920. ops-only?]
  1921. :or {exclude-properties []}}]
  1922. (let [editing-block (when-let [editing-block (state/get-edit-block)]
  1923. (some-> (db/entity [:block/uuid (:block/uuid editing-block)])
  1924. (assoc :block/title (state/get-edit-content))))
  1925. has-unsaved-edits (and editing-block
  1926. (not= (:block/title (db/entity (:db/id editing-block)))
  1927. (state/get-edit-content)))
  1928. target-block (or target-block editing-block)
  1929. block (db/entity (:db/id target-block))
  1930. page (if (:block/name block) block
  1931. (when target-block (:block/page (db/entity (:db/id target-block)))))
  1932. empty-target? (if (true? skip-empty-target?) false
  1933. (string/blank? (:block/title target-block)))
  1934. paste-nested-blocks? (nested-blocks blocks)
  1935. target-block-has-children? (db/has-children? (:block/uuid target-block))
  1936. replace-empty-target? (and empty-target?
  1937. (or (not target-block-has-children?)
  1938. (and target-block-has-children? (= (count blocks) 1))))
  1939. target-block' (if (and empty-target? target-block-has-children? paste-nested-blocks?)
  1940. (or (ldb/get-left-sibling target-block)
  1941. (:block/parent (db/entity (:db/id target-block))))
  1942. target-block)
  1943. sibling? (cond
  1944. (and paste-nested-blocks? empty-target?)
  1945. (= (:block/parent target-block') (:block/parent target-block))
  1946. (some? sibling?)
  1947. sibling?
  1948. target-block-has-children?
  1949. false
  1950. :else
  1951. true)
  1952. transact-blocks! #(ui-outliner-tx/transact!
  1953. {:outliner-op :insert-blocks
  1954. :additional-tx revert-cut-txs}
  1955. (when target-block'
  1956. (let [format (get target-block' :block/format :markdown)
  1957. repo (state/get-current-repo)
  1958. blocks' (map (fn [block]
  1959. (paste-block-cleanup repo block page exclude-properties format content-update-fn keep-uuid?))
  1960. blocks)]
  1961. (outliner-op/insert-blocks! blocks' target-block' {:sibling? sibling?
  1962. :outliner-op :paste
  1963. :replace-empty-target? replace-empty-target?
  1964. :keep-uuid? keep-uuid?}))))]
  1965. (if ops-only?
  1966. (transact-blocks!)
  1967. (p/let [_ (when has-unsaved-edits
  1968. (ui-outliner-tx/transact!
  1969. {:outliner-op :save-block}
  1970. (outliner-save-block! editing-block)))
  1971. result (transact-blocks!)]
  1972. (state/set-block-op-type! nil)
  1973. (when result
  1974. (edit-last-block-after-inserted! result)
  1975. result)))))
  1976. (defn- block-tree->blocks
  1977. "keep-uuid? - maintain the existing :uuid in tree vec"
  1978. [repo tree-vec format keep-uuid? page-name]
  1979. (->> (outliner-core/tree-vec-flatten tree-vec)
  1980. (map (fn [block]
  1981. (let [content (:content block)
  1982. props (into [] (:properties block))
  1983. content* (str (if (= :markdown format) "- " "* ")
  1984. (property-file/insert-properties-when-file-based repo format content props))
  1985. ast (mldoc/->edn content* format)
  1986. blocks (->> (block/extract-blocks ast content* format {:page-name page-name})
  1987. (map wrap-parse-block))
  1988. fst-block (first blocks)
  1989. fst-block (if (and keep-uuid? (uuid? (:uuid block)))
  1990. (assoc fst-block :block/uuid (:uuid block))
  1991. fst-block)]
  1992. (assert fst-block "fst-block shouldn't be nil")
  1993. (assoc fst-block :block/level (:block/level block)))))))
  1994. (defn insert-block-tree
  1995. "`tree-vec`: a vector of blocks.
  1996. A block element: {:content :properties :children [block-1, block-2, ...]}"
  1997. [tree-vec format {:keys [target-block keep-uuid?] :as opts}]
  1998. (let [repo (state/get-current-repo)
  1999. page-id (or (:db/id (:block/page target-block))
  2000. (when (ldb/page? target-block)
  2001. (:db/id target-block)))
  2002. page-name (some-> page-id (db/entity) :block/name)
  2003. blocks (block-tree->blocks repo tree-vec format keep-uuid? page-name)
  2004. blocks (gp-block/with-parent-and-order page-id blocks)]
  2005. (ui-outliner-tx/transact!
  2006. {:outliner-op :paste-blocks}
  2007. (paste-blocks blocks (merge opts {:ops-only? true})))))
  2008. (defn insert-block-tree-after-target
  2009. "`tree-vec`: a vector of blocks.
  2010. A block element: {:content :properties :children [block-1, block-2, ...]}"
  2011. [target-block-id sibling? tree-vec format keep-uuid?]
  2012. (insert-block-tree tree-vec format
  2013. {:target-block (db/entity target-block-id)
  2014. :keep-uuid? keep-uuid?
  2015. :skip-empty-target? true
  2016. :sibling? sibling?}))
  2017. (defn insert-template!
  2018. ([element-id db-id]
  2019. (insert-template! element-id db-id {}))
  2020. ([element-id db-id {:keys [target] :as opts}]
  2021. (let [repo (state/get-current-repo)
  2022. db? (config/db-based-graph? repo)]
  2023. (p/let [block (if (integer? db-id)
  2024. (db-async/<pull repo db-id)
  2025. (db-async/<get-template-by-name (name db-id)))
  2026. block (when (:block/uuid block)
  2027. (db-async/<get-block repo (:block/uuid block)
  2028. {:children? true}))]
  2029. (when (:db/id block)
  2030. (let [journal? (ldb/journal? target)
  2031. target (or target (state/get-edit-block))
  2032. format (get block :block/format :markdown)
  2033. block-uuid (:block/uuid block)
  2034. template-including-parent? (not (false? (:template-including-parent (:block/properties block))))
  2035. blocks (db/get-block-and-children repo block-uuid {:include-property-block? true})
  2036. sorted-blocks (if db?
  2037. (let [blocks' (rest blocks)]
  2038. (cons
  2039. (-> (first blocks')
  2040. (assoc :logseq.property/used-template (:db/id block)))
  2041. (rest blocks')))
  2042. (cons
  2043. (-> (first blocks)
  2044. (update :block/properties-text-values dissoc :template)
  2045. (update :block/properties-order (fn [keys]
  2046. (vec (remove #{:template} keys)))))
  2047. (rest blocks)))
  2048. blocks (cond
  2049. db?
  2050. sorted-blocks
  2051. template-including-parent?
  2052. sorted-blocks
  2053. :else
  2054. (drop 1 sorted-blocks))]
  2055. (when element-id
  2056. (insert-command! element-id "" format {:end-pattern commands/command-trigger}))
  2057. (let [exclude-properties [:id :template :template-including-parent]
  2058. content-update-fn (fn [content]
  2059. (->> content
  2060. (property-file/remove-property-when-file-based repo format "template")
  2061. (property-file/remove-property-when-file-based repo format "template-including-parent")
  2062. template/resolve-dynamic-template!))
  2063. page (if (:block/name block) block
  2064. (when target (:block/page (db/entity (:db/id target)))))
  2065. blocks' (if (config/db-based-graph?)
  2066. blocks
  2067. (map (fn [block]
  2068. (paste-block-cleanup repo block page exclude-properties format content-update-fn false))
  2069. blocks))
  2070. sibling? (:sibling? opts)
  2071. sibling?' (cond
  2072. (some? sibling?)
  2073. sibling?
  2074. (db/has-children? (:block/uuid target))
  2075. false
  2076. :else
  2077. true)]
  2078. (when (seq blocks')
  2079. (try
  2080. (p/let [result (ui-outliner-tx/transact!
  2081. {:outliner-op :insert-blocks
  2082. :created-from-journal-template? journal?}
  2083. (when-not (string/blank? (state/get-edit-content))
  2084. (save-current-block!))
  2085. (outliner-op/insert-blocks! blocks' target
  2086. (assoc opts
  2087. :sibling? sibling?'
  2088. :insert-template? true)))]
  2089. (when result (edit-last-block-after-inserted! result)))
  2090. (catch :default ^js/Error e
  2091. (notification/show!
  2092. [:p.content
  2093. (util/format "Template insert error: %s" (.-message e))]
  2094. :error)))))))))))
  2095. (defn template-on-chosen-handler
  2096. [element-id]
  2097. (fn [template-block]
  2098. (when-let [db-id (:db/id template-block)]
  2099. (insert-template! element-id db-id
  2100. {:replace-empty-target? true}))))
  2101. (defn get-searching-property
  2102. [input]
  2103. (let [value (.-value input)
  2104. pos (util/get-selection-start input)
  2105. postfix (subs value pos)
  2106. end-index (when-let [idx (string/index-of postfix gp-property/colons)]
  2107. (+ (max 0 (count (subs value 0 pos))) idx))
  2108. start-index (or (when-let [p (string/last-index-of (subs value 0 pos) "\n")]
  2109. (inc p))
  2110. 0)]
  2111. {:end-index end-index
  2112. :searching-property (when (and start-index end-index (>= end-index start-index))
  2113. (subs value start-index end-index))}))
  2114. (defn property-on-chosen-handler
  2115. [element-id q]
  2116. (fn [property]
  2117. (when-let [input (gdom/getElement element-id)]
  2118. (let [{:keys [end-index searching-property]} (get-searching-property input)]
  2119. (cursor/move-cursor-to input (+ end-index 2))
  2120. (commands/insert! element-id (str (or property q) gp-property/colons " ")
  2121. {:last-pattern (str searching-property gp-property/colons)})
  2122. (state/clear-editor-action!)
  2123. (js/setTimeout (fn []
  2124. (let [pos (let [input (gdom/getElement element-id)]
  2125. (cursor/get-caret-pos input))]
  2126. (state/set-editor-action-data! {:property (or property q)
  2127. :pos pos})
  2128. (state/set-editor-action! :property-value-search)))
  2129. 50)))))
  2130. (defn property-value-on-chosen-handler
  2131. [element-id q]
  2132. (fn [property-value]
  2133. (commands/insert! element-id (str gp-property/colons " " (or property-value q))
  2134. {:last-pattern (str gp-property/colons " " q)})
  2135. (state/clear-editor-action!)))
  2136. (declare indent-outdent)
  2137. (defn- last-top-level-child?
  2138. [{:keys [id]} block]
  2139. (when id
  2140. (when-let [entity (if-let [id' (parse-uuid (str id))]
  2141. (db/entity [:block/uuid id'])
  2142. (db/get-page id))]
  2143. (= (:block/uuid entity) (:block/uuid (:block/parent block))))))
  2144. (defn insert
  2145. ([insertion]
  2146. (insert insertion false))
  2147. ([insertion auto-complete-enabled?]
  2148. (when (or auto-complete-enabled?
  2149. (not (auto-complete?)))
  2150. (let [^js input (state/get-input)
  2151. selected-start (util/get-selection-start input)
  2152. selected-end (util/get-selection-end input)
  2153. value (.-value input)
  2154. s1 (subs value 0 selected-start)
  2155. s2 (subs value selected-end)]
  2156. (state/set-edit-content! (state/get-edit-input-id)
  2157. (str s1 insertion))
  2158. ;; HACK: save scroll-pos of current pos, then add trailing content
  2159. ;; This logic is also in commands/simple-insert!
  2160. (let [scroll-container (util/nearest-scrollable-container input)
  2161. scroll-pos (.-scrollTop scroll-container)]
  2162. (state/set-edit-content! (state/get-edit-input-id)
  2163. (str s1 insertion s2))
  2164. (cursor/move-cursor-to input (+ selected-start (count insertion)))
  2165. (set! (.-scrollTop scroll-container) scroll-pos))))))
  2166. (defn- keydown-new-line
  2167. "Insert newline to current cursor position"
  2168. []
  2169. (insert "\n"))
  2170. (declare delete-and-update)
  2171. (defn- dwim-in-properties
  2172. [state]
  2173. (when-not (auto-complete?)
  2174. (let [{:keys [block]} (get-state)]
  2175. (when block
  2176. (let [input (state/get-input)
  2177. content (gobj/get input "value")
  2178. format (get (:block (get-state)) :block/format :markdown)
  2179. property-key (:raw-content (thingatpt/property-key-at-point input))
  2180. org? (= format :org)
  2181. move-to-pos (if org? 2 3)]
  2182. (if org?
  2183. (cond
  2184. (and property-key (not= property-key ""))
  2185. (case property-key
  2186. ;; When cursor in "PROPERTIES", add :|: in a new line and move cursor to |
  2187. "PROPERTIES"
  2188. (do (cursor/move-cursor-to-line-end input)
  2189. (insert "\n:: ")
  2190. (cursor/move-cursor-backward input move-to-pos))
  2191. ;; When cursor in "END", new block (respect the previous enter behavior)
  2192. "END"
  2193. (do
  2194. (cursor/move-cursor-to-end input)
  2195. (save-current-block!)
  2196. (insert-new-block! state))
  2197. ;; cursor in other positions of :ke|y: or ke|y::, move to line end for inserting value.
  2198. (if (property-file/property-key-exist?-when-file-based format content property-key)
  2199. (notification/show!
  2200. [:p.content
  2201. (util/format "Property key \"%s\" already exists!" property-key)]
  2202. :error)
  2203. (cursor/move-cursor-to-line-end input)))
  2204. ;; when cursor in empty property key
  2205. (and property-key (= property-key ""))
  2206. (do (delete-and-update
  2207. input
  2208. (cursor/line-beginning-pos input)
  2209. (inc (cursor/line-end-pos input)))
  2210. (property-file/goto-properties-end-when-file-based format input)
  2211. (cursor/move-cursor-to-line-end input))
  2212. :else
  2213. ;;When cursor in other place of PROPERTIES drawer, add :|: in a new line and move cursor to |
  2214. (do
  2215. (insert "\n:: ")
  2216. (cursor/move-cursor-backward input move-to-pos)))
  2217. (insert "\n")))))))
  2218. (defn toggle-list-checkbox
  2219. [{:block/keys [title] :as block} item-content]
  2220. (let [toggle-fn (fn [m x-mark]
  2221. (case (string/lower-case x-mark)
  2222. "[ ]" (str "[x] " item-content)
  2223. "[x]" (str "[ ] " item-content)
  2224. m))
  2225. pattern (re-pattern (str "(\\[[xX ]\\])\\s+?" (gstring/regExpEscape item-content)))
  2226. new-content (string/replace-first title pattern toggle-fn)]
  2227. (save-block-if-changed! block new-content)))
  2228. (defn- dwim-in-list
  2229. []
  2230. (when-not (auto-complete?)
  2231. (let [{:keys [block]} (get-state)]
  2232. (when block
  2233. (let [input (state/get-input)]
  2234. (when-let [item (thingatpt/list-item-at-point input)]
  2235. (let [{:keys [full-content indent bullet checkbox ordered _]} item
  2236. next-bullet (if ordered (str (inc bullet) ".") bullet)
  2237. checkbox (when checkbox "[ ] ")]
  2238. (if (and
  2239. (= (count full-content)
  2240. (+ (if ordered (+ (count (str bullet)) 2) 2) (when checkbox (count checkbox))))
  2241. (string/includes? (.-value input) "\n"))
  2242. (delete-and-update input (cursor/line-beginning-pos input) (cursor/line-end-pos input))
  2243. (let [start-pos (util/get-selection-start input)
  2244. value (.-value input)
  2245. before (subs value 0 start-pos)
  2246. after (subs value start-pos)
  2247. cursor-in-item-content? (and (re-find #"^(\d+){1}\." (last (string/split-lines before)))
  2248. (not (string/blank? (first (string/split-lines after)))))]
  2249. (when-not cursor-in-item-content?
  2250. (cursor/move-cursor-to-line-end input)
  2251. (insert (str "\n" indent next-bullet " " checkbox)))
  2252. (when ordered
  2253. (let [value (.-value input)
  2254. start-pos (util/get-selection-start input)
  2255. after-lists-str (string/trim (subs value start-pos))
  2256. after-lists-str (if cursor-in-item-content?
  2257. (str indent next-bullet " " after-lists-str)
  2258. after-lists-str)
  2259. lines (string/split-lines after-lists-str)
  2260. after-lists-str' (list/re-order-items lines (if cursor-in-item-content? bullet (inc bullet)))
  2261. value' (str (subs value 0 start-pos) "\n" after-lists-str')
  2262. cursor' (if cursor-in-item-content?
  2263. (inc (count (str (subs value 0 start-pos) indent next-bullet " ")))
  2264. (+ (:end item) (count next-bullet) 2))]
  2265. (state/set-edit-content! (state/get-edit-input-id) value')
  2266. (cursor/move-cursor-to input cursor'))))))))))))
  2267. (defn- keydown-new-block
  2268. [state]
  2269. (when-not (auto-complete?)
  2270. (let [{:keys [block config]} (get-state)]
  2271. (when block
  2272. (let [block (db/entity (:db/id block))
  2273. input (state/get-input)
  2274. config (assoc config :keydown-new-block true)
  2275. content (gobj/get input "value")
  2276. pos (cursor/pos input)
  2277. has-right? (ldb/get-right-sibling block)
  2278. db-based? (config/db-based-graph? (state/get-current-repo))
  2279. thing-at-point ;intern is not supported in cljs, need a more elegant solution
  2280. (or (when (thingatpt/get-setting :admonition&src?)
  2281. (thingatpt/admonition&src-at-point input))
  2282. (when (thingatpt/get-setting :markup?)
  2283. (thingatpt/markup-at-point input))
  2284. (when (thingatpt/get-setting :block-ref?)
  2285. (thingatpt/block-ref-at-point input))
  2286. (when (thingatpt/get-setting :page-ref?)
  2287. (thingatpt/page-ref-at-point input))
  2288. (when (and (not db-based?) (thingatpt/get-setting :properties?))
  2289. (thingatpt/properties-at-point input))
  2290. (when (thingatpt/get-setting :list?)
  2291. (and (not (cursor/beginning-of-line? input))
  2292. (thingatpt/list-item-at-point input))))]
  2293. (cond
  2294. thing-at-point
  2295. (case (:type thing-at-point)
  2296. "markup" (let [right-bound (:bounds thing-at-point)]
  2297. (cursor/move-cursor-to
  2298. input
  2299. (+ (string/index-of content right-bound pos)
  2300. (count right-bound))))
  2301. "admonition-block" (keydown-new-line)
  2302. "source-block" (do
  2303. (keydown-new-line)
  2304. (case (:action thing-at-point)
  2305. :into-code-editor
  2306. (state/into-code-editor-mode!)
  2307. nil))
  2308. "block-ref" (open-block-in-sidebar! (:link thing-at-point))
  2309. "page-ref" (when-not (string/blank? (:link thing-at-point))
  2310. (let [page (:link thing-at-point)
  2311. page-name (db-model/get-redirect-page-name page)]
  2312. (p/do!
  2313. (save-current-block!)
  2314. (route-handler/redirect-to-page! page-name))))
  2315. "list-item" (dwim-in-list)
  2316. "properties-drawer" (dwim-in-properties state))
  2317. (and (string/blank? content)
  2318. (own-order-number-list? block)
  2319. (not (some-> (db-model/get-block-parent (:block/uuid block))
  2320. (own-order-number-list?))))
  2321. (remove-block-own-order-list-type! block)
  2322. (and
  2323. (string/blank? content)
  2324. (not has-right?)
  2325. (not (last-top-level-child? config block)))
  2326. (indent-outdent false)
  2327. :else
  2328. (insert-new-block! state)))))))
  2329. (defn- inside-of-single-block
  2330. "When we are in a single block wrapper, we should always insert a new line instead of new block"
  2331. [el]
  2332. (some? (dom/closest el ".single-block")))
  2333. (defn- inside-of-editor-block
  2334. [el]
  2335. (some? (dom/closest el ".block-editor")))
  2336. (defn keydown-new-block-handler [^js e]
  2337. (let [state (get-state)]
  2338. (when (or (nil? (.-target e)) (inside-of-editor-block (.-target e)))
  2339. (if (or (state/doc-mode-enter-for-new-line?) (inside-of-single-block (rum/dom-node state)))
  2340. (keydown-new-line)
  2341. (do
  2342. (.preventDefault e)
  2343. (keydown-new-block state))))))
  2344. (defn keydown-new-line-handler [e]
  2345. (let [state (get-state)]
  2346. (when (or (nil? (.-target e)) (inside-of-editor-block (.-target e)))
  2347. (if (and (state/doc-mode-enter-for-new-line?) (not (inside-of-single-block (rum/dom-node state))))
  2348. (keydown-new-block state)
  2349. (do
  2350. (.preventDefault e)
  2351. (keydown-new-line))))))
  2352. (defn- select-first-last
  2353. "Select first or last block in viewport"
  2354. [direction]
  2355. (let [f (case direction :up last :down first)
  2356. container (if (some-> js/document.activeElement
  2357. (.querySelector ".blocks-container"))
  2358. js/document.activeElement js/document.body)
  2359. block (->> (util/get-blocks-noncollapse container)
  2360. (f))]
  2361. (when block
  2362. (util/scroll-to-block block)
  2363. (state/exit-editing-and-set-selected-blocks! [block]))))
  2364. (defn- select-up-down [direction]
  2365. (let [selected-blocks (state/get-selection-blocks)
  2366. selected (case direction
  2367. :up (first selected-blocks)
  2368. :down (last selected-blocks))
  2369. f (case direction
  2370. :up util/get-prev-block-non-collapsed
  2371. :down util/get-next-block-non-collapsed)
  2372. sibling-block (f selected {:up-down? true
  2373. :exclude-property? true})]
  2374. (when (and sibling-block
  2375. (or (dom/attr sibling-block "blockid") (dom/attr sibling-block "parentblockid")))
  2376. (util/scroll-to-block sibling-block)
  2377. (state/exit-editing-and-set-selected-blocks! [sibling-block]))))
  2378. (defn- active-jtrigger?
  2379. []
  2380. (some-> js/document.activeElement (dom/has-class? "jtrigger")))
  2381. (defn- property-value-node?
  2382. [node]
  2383. (some-> node (dom/has-class? "property-value-container")))
  2384. (defn- focus-trigger
  2385. [_current-block sibling-block]
  2386. (when-let [trigger (first (dom/by-class sibling-block "jtrigger"))]
  2387. (state/clear-edit!)
  2388. (if (or (dom/has-class? trigger "ls-number")
  2389. (dom/has-class? trigger "ls-empty-text-property"))
  2390. (.click trigger)
  2391. (.focus trigger))))
  2392. (defn move-cross-boundary-up-down
  2393. [direction move-opts]
  2394. (let [input (or (:input move-opts) (state/get-input))
  2395. active-element js/document.activeElement
  2396. input-or-active-element (or input active-element)]
  2397. (when input-or-active-element
  2398. (let [repo (state/get-current-repo)
  2399. f (case direction
  2400. :up util/get-prev-block-non-collapsed
  2401. :down util/get-next-block-non-collapsed)
  2402. current-block (util/rec-get-node input-or-active-element "ls-block")
  2403. sibling-block (f current-block {:up-down? true})
  2404. {:block/keys [uuid title format]} (state/get-edit-block)
  2405. format (or format :markdown)
  2406. sibling-block (or (when (property-value-node? sibling-block)
  2407. (first (dom/by-class sibling-block "ls-block")))
  2408. sibling-block)
  2409. property-value-container? (property-value-node? sibling-block)]
  2410. (if sibling-block
  2411. (let [sibling-block-id (dom/attr sibling-block "blockid")
  2412. container-id (some-> (dom/attr sibling-block "containerid") js/parseInt)
  2413. value (state/get-edit-content)]
  2414. (p/do!
  2415. (when (and
  2416. uuid
  2417. (not (state/block-component-editing?))
  2418. (not= (clean-content! repo format title)
  2419. (string/trim value)))
  2420. (save-block! repo uuid value))
  2421. (cond
  2422. (and (dom/has-class? sibling-block "block-add-button")
  2423. (util/rec-get-node current-block "ls-page-title"))
  2424. (.click sibling-block)
  2425. property-value-container?
  2426. (focus-trigger current-block sibling-block)
  2427. :else
  2428. (let [new-uuid (cljs.core/uuid sibling-block-id)
  2429. block (db/entity [:block/uuid new-uuid])]
  2430. (edit-block! block
  2431. (or (:pos move-opts)
  2432. (when input [direction (util/get-line-pos (.-value input) (util/get-selection-start input))])
  2433. 0)
  2434. {:container-id container-id
  2435. :direction direction})))))
  2436. (case direction
  2437. :up (cursor/move-cursor-to input 0)
  2438. :down (cursor/move-cursor-to-end input)))))))
  2439. (defn keydown-up-down-handler
  2440. [direction {:keys [_pos] :as move-opts}]
  2441. (let [input (state/get-input)
  2442. selected-start (util/get-selection-start input)
  2443. selected-end (util/get-selection-end input)
  2444. up? (= direction :up)
  2445. down? (= direction :down)]
  2446. (cond
  2447. (active-jtrigger?)
  2448. (move-cross-boundary-up-down direction move-opts)
  2449. (not= selected-start selected-end)
  2450. (if up?
  2451. (cursor/move-cursor-to input selected-start)
  2452. (cursor/move-cursor-to input selected-end))
  2453. (and input
  2454. (or (and up? (cursor/textarea-cursor-first-row? input))
  2455. (and down? (cursor/textarea-cursor-last-row? input))))
  2456. (move-cross-boundary-up-down direction move-opts)
  2457. :else
  2458. (when input
  2459. (if up?
  2460. (cursor/move-cursor-up input)
  2461. (cursor/move-cursor-down input))))))
  2462. (defn move-to-block-when-cross-boundary
  2463. [direction {:keys [block]}]
  2464. (let [up? (= :left direction)
  2465. pos (if up? :max 0)
  2466. {:block/keys [format uuid] :as block} (or block (state/get-edit-block))
  2467. format (or format :markdown)
  2468. repo (state/get-current-repo)
  2469. editing-block (state/get-editor-block-container)
  2470. f (if up? util/get-prev-block-non-collapsed util/get-next-block-non-collapsed)
  2471. sibling-block (f editing-block)
  2472. sibling-block (or (when (and sibling-block (property-value-node? sibling-block))
  2473. (if (and up? editing-block (gdom/contains sibling-block editing-block))
  2474. (f sibling-block)
  2475. (first (dom/by-class sibling-block "ls-block"))))
  2476. sibling-block)]
  2477. (when sibling-block
  2478. (let [content (:block/title block)
  2479. value (state/get-edit-content)]
  2480. (when (and value (not= (clean-content! repo format content) (string/trim value)))
  2481. (save-block! repo uuid value)))
  2482. (let [sibling-block-id (dom/attr sibling-block "blockid")]
  2483. (cond
  2484. sibling-block-id
  2485. (let [container-id (some-> (dom/attr sibling-block "containerid") js/parseInt)
  2486. block (db/entity repo [:block/uuid (cljs.core/uuid sibling-block-id)])]
  2487. (edit-block! block pos {:container-id container-id}))
  2488. (property-value-node? sibling-block)
  2489. (focus-trigger editing-block sibling-block)
  2490. (and (dom/has-class? sibling-block "block-add-button")
  2491. (util/rec-get-node editing-block "ls-page-title"))
  2492. (.click sibling-block)
  2493. :else
  2494. nil)))))
  2495. (defn keydown-arrow-handler
  2496. [direction]
  2497. (let [input (state/get-input)
  2498. element js/document.activeElement
  2499. selected-start (util/get-selection-start input)
  2500. selected-end (util/get-selection-end input)
  2501. left? (= direction :left)
  2502. right? (= direction :right)
  2503. block (some-> (state/get-edit-block) :db/id db/entity)
  2504. property? (ldb/property? block)]
  2505. (cond
  2506. (and input (not= input element))
  2507. (.focus input)
  2508. (= input element)
  2509. (cond
  2510. (and property? right? (not (cursor/end? input)))
  2511. (cursor/move-cursor-to-end input)
  2512. (and property? left? (not (cursor/start? input)))
  2513. (cursor/move-cursor-to-start input)
  2514. (and property? right? (cursor/end? input)
  2515. (or (not= (:logseq.property/type block) :default)
  2516. (seq (:property/closed-values block))))
  2517. (let [pair (util/rec-get-node input "property-pair")
  2518. jtrigger (when pair (dom/sel1 pair ".property-value-container .jtrigger"))]
  2519. (when jtrigger
  2520. (.focus jtrigger)))
  2521. (not= selected-start selected-end)
  2522. (cond
  2523. left?
  2524. (cursor/move-cursor-to input selected-start)
  2525. :else
  2526. (cursor/move-cursor-to input selected-end))
  2527. (or (and left? (cursor/start? input))
  2528. (and right? (cursor/end? input)))
  2529. (move-to-block-when-cross-boundary direction {})
  2530. :else
  2531. (if left?
  2532. (cursor/move-cursor-backward input)
  2533. (cursor/move-cursor-forward input)))
  2534. :else
  2535. nil)))
  2536. (defn- delete-and-update [^js input start end]
  2537. (util/safe-set-range-text! input "" start end)
  2538. (state/set-edit-content! (state/get-edit-input-id) (.-value input)))
  2539. (defn- delete-concat [current-block]
  2540. (p/let [repo (state/get-current-repo)
  2541. collapsed? (util/collapsed? current-block)
  2542. next-block (when-not collapsed?
  2543. (let [db (db/get-db repo)]
  2544. (when-let [e (or
  2545. ;; first child or next sibling
  2546. (ldb/get-first-child db (:db/id current-block))
  2547. (db-model/get-next db (:db/id current-block)))]
  2548. (db/entity (:db/id e)))))]
  2549. (cond
  2550. collapsed?
  2551. nil
  2552. (nil? next-block)
  2553. nil
  2554. :else
  2555. (let [repo (state/get-current-repo)
  2556. editor-state (assoc (get-state)
  2557. :block-id (:block/uuid next-block)
  2558. :value (:block/title next-block)
  2559. :block-container (util/get-next-block-non-collapsed
  2560. (util/rec-get-node (state/get-input) "ls-block")
  2561. {:exclude-property? true})
  2562. :current-block current-block
  2563. :next-block next-block
  2564. :delete-concat? true)]
  2565. (delete-block-inner! repo editor-state)))))
  2566. (defn keydown-delete-handler
  2567. [_e]
  2568. (let [^js input (state/get-input)
  2569. current-pos (cursor/pos input)
  2570. value (gobj/get input "value")
  2571. end? (= current-pos (count value))
  2572. current-block (state/get-edit-block)
  2573. selected-start (util/get-selection-start input)
  2574. selected-end (util/get-selection-end input)]
  2575. (when current-block
  2576. (cond
  2577. (not= selected-start selected-end)
  2578. (delete-and-update input selected-start selected-end)
  2579. (and end? current-block)
  2580. (let [editor-state (get-state)
  2581. custom-query? (get-in editor-state [:config :custom-query?])]
  2582. (when-not custom-query?
  2583. (delete-concat current-block)))
  2584. :else
  2585. (delete-and-update
  2586. input current-pos (util/safe-inc-current-pos-from-start (.-value input) current-pos))))))
  2587. (defn keydown-backspace-handler
  2588. [cut? e]
  2589. (let [^js input (state/get-input)
  2590. element js/document.activeElement]
  2591. (if (= input element)
  2592. (let [id (state/get-edit-input-id)
  2593. current-pos (cursor/pos input)
  2594. value (gobj/get input "value")
  2595. deleted (and (> current-pos 0)
  2596. (util/nth-safe value (dec current-pos)))
  2597. selected-start (util/get-selection-start input)
  2598. selected-end (util/get-selection-end input)
  2599. block (state/get-edit-block)
  2600. block (db/entity (:db/id block))
  2601. repo (state/get-current-repo)
  2602. top-block? (= (:db/id (or (ldb/get-left-sibling block) (:block/parent block)))
  2603. (:db/id (:block/page block)))
  2604. single-block? (inside-of-single-block (.-target e))
  2605. root-block? (= (:block.temp/container block) (str (:block/uuid block)))]
  2606. (block-handler/mark-last-input-time! repo)
  2607. (cond
  2608. (not= selected-start selected-end)
  2609. (do
  2610. (util/stop e)
  2611. (when cut?
  2612. (js/document.execCommand "copy"))
  2613. (delete-and-update input selected-start selected-end))
  2614. (zero? current-pos)
  2615. (let [editor-state (get-state)
  2616. custom-query? (get-in editor-state [:config :custom-query?])]
  2617. (util/stop e)
  2618. (when (and (not (and top-block? (not (string/blank? value))))
  2619. (not root-block?)
  2620. (not single-block?)
  2621. (not custom-query?))
  2622. (if (own-order-number-list? block)
  2623. (p/do!
  2624. (save-current-block!)
  2625. (remove-block-own-order-list-type! block))
  2626. (delete-block! repo))))
  2627. (and (> current-pos 0)
  2628. (contains? #{commands/command-trigger commands/command-ask}
  2629. (util/nth-safe value (dec current-pos))))
  2630. (do
  2631. (util/stop e)
  2632. (commands/restore-state)
  2633. (delete-and-update input (dec current-pos) current-pos))
  2634. ;; pair
  2635. (and
  2636. deleted
  2637. (contains?
  2638. (set (keys delete-map))
  2639. deleted)
  2640. (>= (count value) (inc current-pos))
  2641. (= (util/nth-safe value current-pos)
  2642. (get delete-map deleted)))
  2643. (do
  2644. (util/stop e)
  2645. (commands/delete-pair! id)
  2646. (cond
  2647. (and (= deleted "[") (state/get-editor-show-page-search?))
  2648. (state/clear-editor-action!)
  2649. (and (= deleted "(") (state/get-editor-show-block-search?))
  2650. (state/clear-editor-action!)
  2651. :else
  2652. nil))
  2653. ;; deleting hashtag
  2654. (and (= deleted "#") (state/get-editor-show-page-search-hashtag?))
  2655. (do
  2656. (state/clear-editor-action!)
  2657. (delete-and-update input (dec current-pos) current-pos))
  2658. ;; just delete
  2659. :else
  2660. (when (and input (not (mobile-util/native-ios?)))
  2661. (util/stop e)
  2662. (delete-and-update
  2663. input (util/safe-dec-current-pos-from-end (.-value input) current-pos) current-pos))))
  2664. false)))
  2665. (defn indent-outdent
  2666. [indent?]
  2667. (let [{:keys [block block-container]} (get-state)]
  2668. (when block
  2669. (let [node block-container
  2670. prev-container-id (get-node-container-id node)
  2671. container-id (get-new-container-id (if indent? :indent :outdent) {})]
  2672. (p/do!
  2673. (block-handler/indent-outdent-blocks! [block] indent? save-current-block!)
  2674. (when (and (not= prev-container-id container-id) container-id)
  2675. (state/set-editing-block-id! [container-id (:block/uuid block)])))))))
  2676. (defn keydown-tab-handler
  2677. [direction]
  2678. (fn [e]
  2679. (cond
  2680. (state/editing?)
  2681. (when-not (state/get-editor-action)
  2682. (util/stop e)
  2683. (indent-outdent (not (= :left direction))))
  2684. (state/selection?)
  2685. (do
  2686. (util/stop e)
  2687. (state/pub-event! [:editor/hide-action-bar])
  2688. (on-tab direction)))
  2689. nil))
  2690. (defn- double-chars-typed?
  2691. [value pos key sym]
  2692. (and (= key sym)
  2693. (>= (count value) 1)
  2694. (> pos 0)
  2695. (= (nth value (dec pos)) sym)
  2696. (if (> (count value) pos)
  2697. (not= (nth value pos) sym)
  2698. true)))
  2699. (defn ^:large-vars/cleanup-todo keydown-not-matched-handler
  2700. "NOTE: Keydown cannot be used on Android platform"
  2701. [format]
  2702. (fn [e _key-code]
  2703. (let [input-id (state/get-edit-input-id)
  2704. input (state/get-input)
  2705. key (gobj/get e "key")
  2706. value (gobj/get input "value")
  2707. ctrlKey (gobj/get e "ctrlKey")
  2708. metaKey (gobj/get e "metaKey")
  2709. pos (cursor/pos input)
  2710. hashtag? (or (surround-by? input "#" " ")
  2711. (surround-by? input "#" :end)
  2712. (= key "#"))
  2713. db-based? (config/db-based-graph? (state/get-current-repo))]
  2714. (when (or (not @(:editor/start-pos @state/state))
  2715. (and key (string/starts-with? key "Arrow")))
  2716. (state/set-state! :editor/start-pos pos))
  2717. (cond
  2718. (and (= :page-search (state/get-editor-action))
  2719. (= key commands/hashtag))
  2720. (do
  2721. (util/stop e)
  2722. (notification/show! "Page name can't include \"#\"." :warning))
  2723. ;; stop accepting edits if the new block is not created yet
  2724. (some? @(:editor/async-unsaved-chars @state/state))
  2725. (do
  2726. (when (= 1 (count (str key)))
  2727. (state/update-state! :editor/async-unsaved-chars
  2728. (fn [s]
  2729. (str s key))))
  2730. (util/stop e))
  2731. (and (contains? #{"ArrowLeft" "ArrowRight"} key)
  2732. (contains? #{:property-search :property-value-search} (state/get-editor-action)))
  2733. (state/clear-editor-action!)
  2734. (and (util/goog-event-is-composing? e true) ;; #3218
  2735. (not hashtag?) ;; #3283 @Rime
  2736. (not (state/get-editor-show-page-search-hashtag?))) ;; #3283 @MacOS pinyin
  2737. nil
  2738. (or ctrlKey metaKey)
  2739. nil
  2740. (and (= key "#")
  2741. (> pos 0)
  2742. (= "#" (util/nth-safe value (dec pos))))
  2743. (state/clear-editor-action!)
  2744. (and (contains? (set/difference (set (keys reversed-autopair-map))
  2745. #{"`"})
  2746. key)
  2747. (= (get-current-input-char input) key))
  2748. (do
  2749. (util/stop e)
  2750. (cursor/move-cursor-forward input))
  2751. (and (autopair-when-selected key) (string/blank? (util/get-selected-text)))
  2752. nil
  2753. (some? @(:editor/action @state/state))
  2754. nil
  2755. (and (not (string/blank? (util/get-selected-text)))
  2756. (contains? keycode/left-square-brackets-keys key))
  2757. (do
  2758. (autopair input-id "[" format nil)
  2759. (util/stop e))
  2760. (and (not (string/blank? (util/get-selected-text)))
  2761. (contains? keycode/left-paren-keys key))
  2762. (do (util/stop e)
  2763. (autopair input-id "(" format nil))
  2764. ;; If you type `xyz`, the last backtick should close the first and not add another autopair
  2765. ;; If you type several backticks in a row, each one should autopair to accommodate multiline code (```)
  2766. (-> (keys autopair-map)
  2767. set
  2768. (disj "(")
  2769. (contains? key)
  2770. (or (autopair-left-paren? input key)))
  2771. (let [curr (get-current-input-char input)
  2772. prev (util/nth-safe value (dec pos))]
  2773. (util/stop e)
  2774. (if (and (= key "`") (= "`" curr) (not= "`" prev))
  2775. (cursor/move-cursor-forward input)
  2776. (autopair input-id key format nil)))
  2777. ; `;;` to add or change property for db graphs
  2778. (let [sym ";"]
  2779. (and db-based? (double-chars-typed? value pos key sym)))
  2780. (state/pub-event! [:editor/new-property])
  2781. (let [sym "$"]
  2782. (double-chars-typed? value pos key sym))
  2783. (commands/simple-insert! input-id "$$" {:backward-pos 2})
  2784. (let [sym "^"]
  2785. (double-chars-typed? value pos key sym))
  2786. (commands/simple-insert! input-id "^^" {:backward-pos 2})
  2787. :else
  2788. nil))))
  2789. (defn- input-page-ref?
  2790. [k current-pos blank-selected? last-key-code]
  2791. (and blank-selected?
  2792. (contains? keycode/left-square-brackets-keys k)
  2793. (= (:key last-key-code) k)
  2794. (> current-pos 0)))
  2795. (defn- default-case-for-keyup-handler
  2796. [input current-pos k code is-processed?]
  2797. (let [last-key-code (state/get-last-key-code)
  2798. blank-selected? (string/blank? (util/get-selected-text))
  2799. non-enter-processed? (and is-processed? ;; #3251
  2800. (not= code keycode/enter-code)) ;; #3459
  2801. editor-action (state/get-editor-action)]
  2802. (if (and (= editor-action :page-search-hashtag)
  2803. (input-page-ref? k current-pos blank-selected? last-key-code))
  2804. (do
  2805. (commands/handle-step [:editor/input page-ref/right-brackets {:last-pattern :skip-check
  2806. :backward-pos 2}])
  2807. (commands/handle-step [:editor/search-page])
  2808. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
  2809. (when (and (not editor-action) (not non-enter-processed?))
  2810. (cond
  2811. ;; When you type text inside square brackets
  2812. (and (not (contains? #{"ArrowDown" "ArrowLeft" "ArrowRight" "ArrowUp" "Escape"} k))
  2813. (wrapped-by? input page-ref/left-brackets page-ref/right-brackets))
  2814. (let [orig-pos (cursor/get-caret-pos input)
  2815. value (gobj/get input "value")
  2816. square-pos (string/last-index-of (subs value 0 (:pos orig-pos)) page-ref/left-brackets)
  2817. pos (+ square-pos 2)
  2818. _ (state/set-editor-last-pos! pos)
  2819. pos (assoc orig-pos :pos pos)
  2820. command-step (if (= \# (util/nth-safe value (dec square-pos)))
  2821. :editor/search-page-hashtag
  2822. :editor/search-page)]
  2823. (commands/handle-step [command-step])
  2824. (state/set-editor-action-data! {:pos pos}))
  2825. ;; Handle non-ascii square brackets
  2826. (and (input-page-ref? k current-pos blank-selected? last-key-code)
  2827. (not (wrapped-by? input page-ref/left-brackets page-ref/right-brackets)))
  2828. (do
  2829. (commands/handle-step [:editor/input page-ref/left-and-right-brackets {:backward-truncate-number 2
  2830. :backward-pos 2}])
  2831. (commands/handle-step [:editor/search-page])
  2832. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
  2833. ;; Handle non-ascii parentheses
  2834. (and blank-selected?
  2835. (contains? keycode/left-paren-keys k)
  2836. (= (:key last-key-code) k)
  2837. (> current-pos 0)
  2838. (not (wrapped-by? input block-ref/left-parens block-ref/right-parens)))
  2839. (do
  2840. (commands/handle-step [:editor/input block-ref/left-and-right-parens {:backward-truncate-number 2
  2841. :backward-pos 2}])
  2842. (commands/handle-step [:editor/search-block :reference])
  2843. (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
  2844. :else
  2845. nil)))))
  2846. (defn keyup-handler
  2847. [_state input]
  2848. (fn [e key-code]
  2849. (when-not (util/goog-event-is-composing? e)
  2850. (let [db-based? (config/db-based-graph?)
  2851. current-pos (cursor/pos input)
  2852. value (gobj/get input "value")
  2853. c (util/nth-safe value (dec current-pos))
  2854. [key-code k code is-processed?]
  2855. (if (and c
  2856. (mobile-util/native-android?)
  2857. (or (= key-code 229)
  2858. (= key-code 0)))
  2859. [(.charCodeAt value (dec current-pos))
  2860. c
  2861. (cond
  2862. (= c " ")
  2863. "Space"
  2864. (parse-long c)
  2865. (str "Digit" c)
  2866. :else
  2867. (str "Key" (string/upper-case c)))
  2868. false]
  2869. [key-code
  2870. (gobj/get e "key")
  2871. (if (mobile-util/native-android?)
  2872. (gobj/get e "key")
  2873. (gobj/getValueByKeys e "event_" "code"))
  2874. ;; #3440
  2875. (util/goog-event-is-composing? e true)])]
  2876. (cond
  2877. (and db-based? (= value "``````")) ; turn this block into a code block
  2878. (do
  2879. (state/set-edit-content! (.-id input) "")
  2880. (state/pub-event! [:editor/upsert-type-block {:block (assoc (state/get-edit-block) :block/title "")
  2881. :type :code
  2882. :update-current-block? true}]))
  2883. (and db-based? (= value ">")) ; turn this block into a quote block
  2884. (do
  2885. (state/set-edit-content! (.-id input) "")
  2886. (state/pub-event! [:editor/upsert-type-block {:block (assoc (state/get-edit-block) :block/title "")
  2887. :type :quote
  2888. :update-current-block? true}]))
  2889. ;; When you type something after /
  2890. (and (= :commands (state/get-editor-action)) (not= k commands/command-trigger))
  2891. (if (= commands/command-trigger (second (re-find #"(\S+)\s+$" value)))
  2892. (state/clear-editor-action!)
  2893. (let [command (get-last-command input)
  2894. matched-commands (get-matched-commands command)]
  2895. (if (seq matched-commands)
  2896. (commands/set-matched-commands! command matched-commands)
  2897. (if (> (- (count command) (count @commands/*latest-matched-command)) 2)
  2898. (state/clear-editor-action!)
  2899. (reset! commands/*matched-commands nil)))))
  2900. :else
  2901. (default-case-for-keyup-handler input current-pos k code is-processed?))
  2902. (close-autocomplete-if-outside input)
  2903. (when-not (or (= k "Shift") is-processed?)
  2904. (state/set-last-key-code! {:key-code key-code
  2905. :code code
  2906. :key k
  2907. :shift? (.-shiftKey e)}))
  2908. (when-not (state/get-editor-action)
  2909. (state/set-editor-last-pos! current-pos))))))
  2910. (defn editor-on-click!
  2911. [id]
  2912. (fn [_e]
  2913. (let [input (gdom/getElement id)]
  2914. (util/scroll-editor-cursor input)
  2915. (close-autocomplete-if-outside input))))
  2916. (defn editor-on-change!
  2917. [block id search-timeout]
  2918. (fn [e]
  2919. (let [editor-action (state/get-editor-action)]
  2920. (if (= :block-search editor-action)
  2921. (let [timeout 50]
  2922. (when @search-timeout
  2923. (js/clearTimeout @search-timeout))
  2924. (reset! search-timeout
  2925. (js/setTimeout
  2926. #(edit-box-on-change! e block id)
  2927. timeout)))
  2928. (let [input (gdom/getElement id)]
  2929. (edit-box-on-change! e block id)
  2930. (when-not editor-action
  2931. (util/scroll-editor-cursor input)))))))
  2932. (defn- cut-blocks-and-clear-selections!
  2933. [copy?]
  2934. (when-not (get-in @state/state [:ui/find-in-page :active?])
  2935. (cut-selection-blocks copy?)
  2936. (clear-selection!)))
  2937. (defn shortcut-copy-selection
  2938. [_e]
  2939. (copy-selection-blocks true))
  2940. (defn shortcut-cut-selection
  2941. [e]
  2942. (when-not (util/input? (.-target e))
  2943. (util/stop e)
  2944. (cut-blocks-and-clear-selections! true)))
  2945. (defn shortcut-delete-selection
  2946. [e]
  2947. (when-not (util/input? (.-target e))
  2948. (util/stop e)
  2949. (cut-blocks-and-clear-selections! false)))
  2950. (defn- copy-current-block-ref
  2951. [format]
  2952. (when-let [current-block (state/get-edit-block)]
  2953. (when-let [block-id (:block/uuid current-block)]
  2954. (let [db? (config/db-based-graph? (state/get-current-repo))]
  2955. (if (= format "embed")
  2956. (if db?
  2957. (p/do!
  2958. (save-current-block!)
  2959. (util/copy-to-clipboard! (ref/->page-ref block-id)
  2960. {:graph (state/get-current-repo)
  2961. :blocks [{:block/uuid (:block/uuid current-block)}]
  2962. :embed-block? true}))
  2963. (copy-block-ref! block-id #(str "{{embed ((" % "))}}")))
  2964. (copy-block-ref! block-id
  2965. (if db?
  2966. ref/->page-ref
  2967. ref/->block-ref)))))))
  2968. (defn copy-current-block-embed []
  2969. (copy-current-block-ref "embed"))
  2970. (defn shortcut-copy
  2971. "shortcut copy action:
  2972. * when in selection mode, copy selected blocks
  2973. * when in edit mode but no text selected, copy current block ref
  2974. * when in edit mode with text selected, copy selected text as normal
  2975. * when text is selected on a PDF, copy the highlighted text"
  2976. [e]
  2977. (when-not (auto-complete?)
  2978. (cond
  2979. (state/selection?)
  2980. (shortcut-copy-selection e)
  2981. (and (state/editing?) (nil? (:editor/code-block-context @state/state)))
  2982. (let [input (state/get-input)
  2983. selected-start (util/get-selection-start input)
  2984. selected-end (util/get-selection-end input)]
  2985. (save-current-block!)
  2986. (when (= selected-start selected-end)
  2987. (copy-current-block-ref "ref")))
  2988. (and (state/get-current-pdf)
  2989. (.closest (.. js/window getSelection -baseNode -parentElement) ".pdfViewer"))
  2990. (util/copy-to-clipboard!
  2991. (pdf-utils/fix-selection-text-breakline (.. js/window getSelection toString))
  2992. nil))))
  2993. (defn shortcut-copy-text
  2994. "shortcut copy action:
  2995. * when in selection mode, copy selected blocks
  2996. * when in edit mode with text selected, copy selected text as normal"
  2997. [_e]
  2998. (when-not (auto-complete?)
  2999. (cond
  3000. (state/selection?)
  3001. (copy-selection-blocks false)
  3002. :else
  3003. (js/document.execCommand "copy"))))
  3004. (defn whiteboard?
  3005. []
  3006. (and (db-model/whiteboard-page? (state/get-current-page))
  3007. (.closest (.-activeElement js/document) ".logseq-tldraw")))
  3008. (defn shortcut-cut
  3009. "shortcut cut action:
  3010. * when in selection mode, cut selected blocks
  3011. * when in edit mode with text selected, cut selected text
  3012. * otherwise nothing need to be handled."
  3013. [e]
  3014. (cond
  3015. (state/selection?)
  3016. (shortcut-cut-selection e)
  3017. (and (state/editing?) (util/input-text-selected?
  3018. (gdom/getElement (state/get-edit-input-id))))
  3019. (keydown-backspace-handler true e)
  3020. (whiteboard?)
  3021. (.cut (state/active-tldraw-app))
  3022. :else
  3023. nil))
  3024. (defn delete-selection
  3025. [e]
  3026. (cond
  3027. (state/selection?)
  3028. (shortcut-delete-selection e)
  3029. (and (whiteboard?) (not (state/editing?)))
  3030. (.deleteShapes (.-api ^js (state/active-tldraw-app)))
  3031. :else
  3032. nil))
  3033. (defn editor-delete
  3034. [e]
  3035. (when (state/editing?)
  3036. (util/stop e)
  3037. (keydown-delete-handler e)))
  3038. (defn editor-backspace
  3039. [e]
  3040. (when (state/editing?)
  3041. (keydown-backspace-handler false e)))
  3042. (defn- in-page-preview?
  3043. []
  3044. (some-> js/document.activeElement
  3045. (.closest ".ls-preview-popup")
  3046. (nil?) (not)))
  3047. (defn shortcut-up-down [direction]
  3048. (fn [e]
  3049. (state/pub-event! [:editor/hide-action-bar])
  3050. (when (and (not (auto-complete?))
  3051. (or (in-page-preview?)
  3052. (not (in-shui-popup?)))
  3053. (not (state/get-timestamp-block)))
  3054. (util/stop e)
  3055. (cond
  3056. (or (state/editing?) (active-jtrigger?))
  3057. (keydown-up-down-handler direction {})
  3058. (state/selection?)
  3059. (select-up-down direction)
  3060. ;; if there is an edit-input-id set, we are probably still on editing mode,
  3061. ;; that is not fully initialized
  3062. (not (state/get-edit-input-id))
  3063. (select-first-last direction)))
  3064. nil))
  3065. (defn shortcut-select-up-down [direction]
  3066. (fn [e]
  3067. (util/stop e)
  3068. (if (state/editing?)
  3069. (let [input (state/get-input)
  3070. selected-start (util/get-selection-start input)
  3071. selected-end (util/get-selection-end input)
  3072. [anchor cursor] (case (util/get-selection-direction input)
  3073. "backward" [selected-end selected-start]
  3074. [selected-start selected-end])
  3075. cursor-rect (cursor/get-caret-pos input cursor)]
  3076. (if
  3077. ;; if the move is to cross block boundary, select the whole block
  3078. (or (and (= direction :up) (cursor/textarea-cursor-rect-first-row? cursor-rect))
  3079. (and (= direction :down) (cursor/textarea-cursor-rect-last-row? cursor-rect)))
  3080. (select-block-up-down direction)
  3081. ;; simulate text selection
  3082. (cursor/select-up-down input direction anchor cursor-rect)))
  3083. (select-block-up-down direction))))
  3084. (defn editor-commands-popup-exists?
  3085. []
  3086. (popup-exists? "editor.commands"))
  3087. (defn open-selected-blocks-in-sidebar!
  3088. []
  3089. (doseq [id (state/get-selection-block-ids)]
  3090. (state/sidebar-add-block! (state/get-current-repo) id :block)))
  3091. (defn open-selected-block!
  3092. [direction e]
  3093. (when-not (auto-complete?)
  3094. (let [selected-blocks (state/get-selection-blocks)
  3095. f (case direction :left first :right last)
  3096. node (some-> selected-blocks f)]
  3097. (if (some-> node (dom/has-class? "block-add-button"))
  3098. (.click node)
  3099. (when-let [block-id (some-> node (dom/attr "blockid") uuid)]
  3100. (util/stop e)
  3101. (let [block {:block/uuid block-id}
  3102. left? (= direction :left)
  3103. opts {:container-id (some-> node (dom/attr "containerid") (parse-long))
  3104. :event e}]
  3105. (edit-block! block (if left? 0 :max) opts)))))))
  3106. (defn shortcut-left-right [direction]
  3107. (fn [e]
  3108. (when (and (not (auto-complete?))
  3109. (not (state/get-timestamp-block)))
  3110. (cond
  3111. (state/editing?)
  3112. (do
  3113. (util/stop e)
  3114. (keydown-arrow-handler direction))
  3115. (state/selection?)
  3116. (do
  3117. (util/stop e)
  3118. (open-selected-block! direction e))
  3119. :else
  3120. nil))))
  3121. (defn clear-block-content! []
  3122. (save-current-block! {:force? true})
  3123. (state/set-edit-content! (state/get-edit-input-id) ""))
  3124. (defn kill-line-before! []
  3125. (save-current-block! {:force? true})
  3126. (util/kill-line-before! (state/get-input)))
  3127. (defn kill-line-after! []
  3128. (save-current-block! {:force? true})
  3129. (util/kill-line-after! (state/get-input)))
  3130. (defn beginning-of-block []
  3131. (cursor/move-cursor-to (state/get-input) 0))
  3132. (defn end-of-block []
  3133. (cursor/move-cursor-to-end (state/get-input)))
  3134. (defn cursor-forward-word []
  3135. (cursor/move-cursor-forward-by-word (state/get-input)))
  3136. (defn cursor-backward-word []
  3137. (cursor/move-cursor-backward-by-word (state/get-input)))
  3138. (defn backward-kill-word []
  3139. (let [input (state/get-input)]
  3140. (save-current-block! {:force? true})
  3141. (util/backward-kill-word input)
  3142. (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
  3143. (defn forward-kill-word []
  3144. (let [input (state/get-input)]
  3145. (save-current-block! {:force? true})
  3146. (util/forward-kill-word input)
  3147. (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
  3148. (defn block-with-title?
  3149. [format content semantic?]
  3150. (and (string/includes? content "\n")
  3151. (if semantic?
  3152. (let [ast (mldoc/->edn content format)
  3153. first-elem-type (first (ffirst ast))]
  3154. (mldoc/block-with-title? first-elem-type))
  3155. true)))
  3156. (defn- db-collapsable?
  3157. [block]
  3158. (let [class-properties (:classes-properties (outliner-property/get-block-classes-properties (db/get-db) (:db/id block)))
  3159. db (db/get-db)
  3160. attributes (set (remove #{:block/alias} db-property/db-attribute-properties))
  3161. properties (->> (:block.temp/property-keys block)
  3162. (map (partial entity-plus/entity-memoized db))
  3163. (concat class-properties)
  3164. (remove (fn [e] (attributes (:db/ident e))))
  3165. (remove outliner-property/property-with-other-position?)
  3166. (remove (fn [e] (:logseq.property/hide? e)))
  3167. (remove nil?))]
  3168. (or (seq properties)
  3169. (ldb/class-instance? (entity-plus/entity-memoized db :logseq.class/Query) block))))
  3170. (defn collapsable?
  3171. ([block-id]
  3172. (collapsable? block-id {}))
  3173. ([block-id {:keys [semantic? ignore-children?]
  3174. :or {semantic? false
  3175. ignore-children? false}}]
  3176. (when block-id
  3177. (let [repo (state/get-current-repo)]
  3178. (if-let [block (db/entity [:block/uuid block-id])]
  3179. (let [db-based? (config/db-based-graph? repo)]
  3180. (or (if ignore-children? false (db-model/has-children? block-id))
  3181. (and db-based? (db-collapsable? block))
  3182. (and (not db-based?)
  3183. (or (file-editor-handler/valid-dsl-query-block? block)
  3184. (file-editor-handler/valid-custom-query-block? block)))
  3185. (and
  3186. (:outliner/block-title-collapse-enabled? (state/get-config))
  3187. (block-with-title? (get block :block/format :markdown)
  3188. (:block/title block)
  3189. semantic?))))
  3190. false)))))
  3191. (defn <all-blocks-with-level
  3192. "Return all blocks associated with correct level
  3193. if :root-block is not nil, only return root block with its children
  3194. if :expanded? true, return expanded children
  3195. if :collapse? true, return without any collapsed children
  3196. if :incremental? true, collapse/expand will be step by step
  3197. for example:
  3198. - a
  3199. - b (collapsed)
  3200. - c
  3201. - d
  3202. - e
  3203. return:
  3204. blocks
  3205. [{:block a :level 1}
  3206. {:block b :level 2}
  3207. {:block e :level 2}]"
  3208. [{:keys [collapse? expanded? incremental? root-block page]
  3209. :or {collapse? false expanded? false incremental? true root-block nil}}]
  3210. (when-let [page (or page
  3211. (state/get-current-page)
  3212. (date/today))]
  3213. (p/let [block-id (or root-block (parse-uuid page))
  3214. page-id (let [page-entity (db/get-page page)]
  3215. (if (config/db-based-graph?)
  3216. (when (ldb/page? page-entity)
  3217. (:block/uuid page-entity))
  3218. (when-not block-id
  3219. (:block/uuid page-entity))))
  3220. repo (state/get-current-repo)
  3221. _ (db-async/<get-block repo (or block-id page-id)
  3222. {:children? true
  3223. :include-collapsed-children? true})
  3224. entity (db/entity [:block/uuid (or block-id page-id)])
  3225. result (or (:block/_page entity)
  3226. (rest (db/get-block-and-children repo (:block/uuid entity))))
  3227. blocks (if page-id
  3228. result
  3229. (cons (db/entity [:block/uuid block-id]) result))
  3230. root-block (or block-id root-block)]
  3231. (if incremental?
  3232. (let [blocks (tree/blocks->vec-tree blocks (or block-id page-id))]
  3233. (->>
  3234. (cond->> blocks
  3235. root-block
  3236. (map (fn find [root]
  3237. (if (= root-block (:block/uuid root))
  3238. root
  3239. (first (filter find (:block/children root []))))))
  3240. collapse?
  3241. (w/postwalk
  3242. (fn [b]
  3243. (if (and (map? b)
  3244. (util/collapsed? b)
  3245. (not= root-block (:block/uuid b)))
  3246. (assoc b :block/children []) b)))
  3247. true
  3248. (mapcat (fn [x] (tree-seq map? :block/children x)))
  3249. expanded?
  3250. (filter (fn [b] (collapsable? (:block/uuid b))))
  3251. true
  3252. (map (fn [x] (dissoc x :block/children))))
  3253. (remove nil?)))
  3254. (cond->> blocks
  3255. collapse?
  3256. (filter util/collapsed?)
  3257. expanded?
  3258. (filter (fn [b] (collapsable? (:block/uuid b))))
  3259. true
  3260. (remove nil?))))))
  3261. (defn- skip-collapsing-in-db?
  3262. []
  3263. (let [config (last (state/get-editor-args))]
  3264. (:ref? config)))
  3265. (defn set-blocks-collapsed!
  3266. [block-ids value]
  3267. (let [block-ids (map (fn [block-id] (if (string? block-id) (uuid block-id) block-id)) block-ids)
  3268. repo (state/get-current-repo)
  3269. value (boolean value)]
  3270. (when repo
  3271. (save-current-block!) ;; Save the input contents before collapsing
  3272. (ui-outliner-tx/transact! ;; Save the new collapsed state as an undo transaction (if it changed)
  3273. {:outliner-op :collapse-expand-blocks}
  3274. (doseq [block-id block-ids]
  3275. (when-let [block (db/entity [:block/uuid block-id])]
  3276. (let [current-value (boolean (:block/collapsed? block))]
  3277. (when-not (= current-value value)
  3278. (let [block {:block/uuid block-id
  3279. :block/collapsed? value}]
  3280. (outliner-save-block! block {:outliner-op :collapse-expand-blocks})))))))
  3281. (doseq [block-id block-ids]
  3282. (state/set-collapsed-block! block-id value)))))
  3283. (defn collapse-block! [block-id]
  3284. (when (collapsable? block-id)
  3285. (when-not (skip-collapsing-in-db?)
  3286. (set-blocks-collapsed! [block-id] true))
  3287. (state/set-collapsed-block! block-id true)))
  3288. (defn expand-block! [block-id & {:keys [skip-db-collpsing?]}]
  3289. (let [repo (state/get-current-repo)]
  3290. (p/do!
  3291. (db-async/<get-block repo block-id {:children? true
  3292. :include-collapsed-children? true})
  3293. (when-not (or skip-db-collpsing? (skip-collapsing-in-db?))
  3294. (set-blocks-collapsed! [block-id] false))
  3295. (state/set-collapsed-block! block-id false))))
  3296. (defn expand!
  3297. ([e] (expand! e false))
  3298. ([e clear-selection?]
  3299. (util/stop e)
  3300. (cond
  3301. (state/editing?)
  3302. (when-let [block-id (:block/uuid (state/get-edit-block))]
  3303. (expand-block! block-id))
  3304. (state/selection?)
  3305. (do
  3306. (->> (get-selected-blocks)
  3307. (map (fn [dom]
  3308. (-> (dom/attr dom "blockid")
  3309. uuid
  3310. expand-block!)))
  3311. doall)
  3312. (and clear-selection? (clear-selection!)))
  3313. (whiteboard?)
  3314. (.setCollapsed (.-api ^js (state/active-tldraw-app)) false)
  3315. :else
  3316. ;; expand one level
  3317. (p/let [blocks-with-level (<all-blocks-with-level {})
  3318. max-level (or (apply max (map :block/level blocks-with-level)) 99)]
  3319. (loop [level 1]
  3320. (if (> level max-level)
  3321. nil
  3322. (let [blocks-to-expand (->> blocks-with-level
  3323. (filter (fn [b] (= (:block/level b) level)))
  3324. (filter util/collapsed?))]
  3325. (if (empty? blocks-to-expand)
  3326. (recur (inc level))
  3327. (doseq [{:block/keys [uuid]} blocks-to-expand]
  3328. (expand-block! uuid))))))))))
  3329. (defn collapse!
  3330. ([e] (collapse! e false))
  3331. ([e clear-selection?]
  3332. (when e (util/stop e))
  3333. (cond
  3334. (state/editing?)
  3335. (when-let [block-id (:block/uuid (state/get-edit-block))]
  3336. (collapse-block! block-id))
  3337. (state/selection?)
  3338. (do
  3339. (->> (get-selected-blocks)
  3340. (map (fn [dom]
  3341. (-> (dom/attr dom "blockid")
  3342. uuid
  3343. collapse-block!)))
  3344. doall)
  3345. (and clear-selection? (clear-selection!)))
  3346. (whiteboard?)
  3347. (.setCollapsed (.-api ^js (state/active-tldraw-app)) true)
  3348. :else
  3349. ;; collapse by one level from outside
  3350. (p/let [blocks-with-level
  3351. (<all-blocks-with-level {:collapse? true})
  3352. max-level (or (apply max (map :block/level blocks-with-level)) 99)]
  3353. (loop [level max-level]
  3354. (if (zero? level)
  3355. nil
  3356. (let [blocks-to-collapse
  3357. (->> blocks-with-level
  3358. (filter (fn [b] (= (:block/level b) level)))
  3359. (filter (fn [b] (collapsable? (:block/uuid b)))))]
  3360. (if (empty? blocks-to-collapse)
  3361. (recur (dec level))
  3362. (doseq [{:block/keys [uuid]} blocks-to-collapse]
  3363. (collapse-block! uuid))))))))))
  3364. (defn toggle-collapse!
  3365. ([e] (toggle-collapse! e false))
  3366. ([e clear-selection?]
  3367. (when e (util/stop e))
  3368. (cond
  3369. (state/editing?)
  3370. (when-let [block (state/get-edit-block)]
  3371. ;; get-edit-block doesn't track the latest collapsed state, so we need to reload from db.
  3372. (let [block-id (:block/uuid block)
  3373. block (db/entity [:block/uuid block-id])]
  3374. (if (:block/collapsed? block)
  3375. (expand! e clear-selection?)
  3376. (collapse! e clear-selection?))))
  3377. (state/selection?)
  3378. (do
  3379. (let [block-ids (map #(-> % (dom/attr "blockid") uuid) (get-selected-blocks))
  3380. first-block-id (first block-ids)]
  3381. (when first-block-id
  3382. ;; If multiple blocks are selected, they may not have all the same collapsed state.
  3383. ;; For simplicity, use the first block's state to decide whether to collapse/expand all.
  3384. (let [first-block (db/entity [:block/uuid first-block-id])]
  3385. (if (:block/collapsed? first-block)
  3386. (doseq [block-id block-ids] (expand-block! block-id))
  3387. (doseq [block-id block-ids] (collapse-block! block-id))))))
  3388. (and clear-selection? (clear-selection!)))
  3389. :else
  3390. ;; If no block is being edited or selected, the "toggle" action doesn't make sense,
  3391. ;; so we no-op here, unlike in the expand! & collapse! functions.
  3392. nil)))
  3393. (defn collapse-all!
  3394. ([]
  3395. (collapse-all! nil {}))
  3396. ([block-id {:keys [collapse-self?]
  3397. :or {collapse-self? true}}]
  3398. (p/let [blocks (<all-blocks-with-level {:incremental? false
  3399. :expanded? true
  3400. :root-block block-id})
  3401. block-ids (cond->> (mapv :block/uuid blocks)
  3402. (not collapse-self?)
  3403. (remove #{block-id}))]
  3404. (set-blocks-collapsed! block-ids true))))
  3405. (defn expand-all!
  3406. ([]
  3407. (expand-all! nil))
  3408. ([block-id]
  3409. (p/let [blocks (<all-blocks-with-level {:incremental? false
  3410. :collapse? true
  3411. :root-block block-id})
  3412. block-ids (map :block/uuid blocks)]
  3413. (set-blocks-collapsed! block-ids false))))
  3414. (defn collapse-all-selection!
  3415. []
  3416. (p/let [result (p/all
  3417. (map #(<all-blocks-with-level {:incremental? false
  3418. :expanded? true
  3419. :root-block %})
  3420. (get-selected-toplevel-block-uuids)))
  3421. block-ids (->> result
  3422. (apply concat)
  3423. (map :block/uuid)
  3424. distinct)]
  3425. (set-blocks-collapsed! block-ids true)))
  3426. (defn expand-all-selection!
  3427. []
  3428. (->
  3429. (p/let [select-ids (get-selected-toplevel-block-uuids)
  3430. result (p/all
  3431. (map #(<all-blocks-with-level {:incremental? false
  3432. :collapse? true
  3433. :root-block %})
  3434. select-ids))
  3435. block-ids (->> result
  3436. (apply concat)
  3437. (map :block/uuid)
  3438. distinct)]
  3439. (set-blocks-collapsed! block-ids false))
  3440. (p/catch (fn [e]
  3441. (js/console.error e)))))
  3442. (defn toggle-open! []
  3443. (p/let [blocks (<all-blocks-with-level {:incremental? false
  3444. :collapse? true})
  3445. all-expanded? (empty? blocks)]
  3446. (if all-expanded?
  3447. (collapse-all!)
  3448. (expand-all!))))
  3449. (defn toggle-open-block-children! [block-id]
  3450. (p/let [blocks (<all-blocks-with-level {:incremental? false
  3451. :collapse? true
  3452. :root-block block-id})
  3453. all-expanded? (empty? blocks)]
  3454. (if all-expanded?
  3455. (collapse-all! block-id {:collapse-self? false})
  3456. (expand-all! block-id))))
  3457. (defn select-all-blocks!
  3458. [{:keys [page]}]
  3459. (p/do!
  3460. (if-let [current-input-id (state/get-edit-input-id)]
  3461. (let [input (gdom/getElement current-input-id)
  3462. blocks-container (util/rec-get-blocks-container input)
  3463. blocks (dom/by-class blocks-container "ls-block")]
  3464. (state/exit-editing-and-set-selected-blocks! blocks))
  3465. (p/let [blocks (<all-blocks-with-level {:page page
  3466. :collapse? true})]
  3467. (->> blocks
  3468. (map (fn [b] (or (some-> (:db/id (:block/link b)) db/entity) b)))
  3469. (mapcat (fn [b] (util/get-blocks-by-id (:block/uuid b))))
  3470. state/exit-editing-and-set-selected-blocks!)))
  3471. (state/set-state! :selection/selected-all? true)))
  3472. (defn select-parent [e]
  3473. (let [edit-input (some-> (state/get-edit-input-id) gdom/getElement)
  3474. edit-block (state/get-edit-block)
  3475. target-element (.-nodeName (.-target e))]
  3476. (cond
  3477. ;; editing block fully selected
  3478. (and edit-block edit-input
  3479. (= (util/get-selected-text) (.-value edit-input)))
  3480. (do
  3481. (util/stop e)
  3482. (state/exit-editing-and-set-selected-blocks!
  3483. [(util/get-first-block-by-id (:block/uuid edit-block))]))
  3484. edit-block
  3485. nil
  3486. ;; Focusing other input element, e.g. when editing page title.
  3487. (contains? #{"INPUT" "TEXTAREA"} target-element)
  3488. nil
  3489. (whiteboard?)
  3490. (do
  3491. (util/stop e)
  3492. (.selectAll (.-api ^js (state/active-tldraw-app))))
  3493. :else
  3494. (do
  3495. (util/stop e)
  3496. (when-not @(:selection/selected-all? @state/state)
  3497. (if-let [block-id (some-> (first (state/get-selection-blocks))
  3498. (dom/attr "blockid")
  3499. uuid)]
  3500. (when-let [block (db/entity [:block/uuid block-id])]
  3501. (let [parent (:block/parent block)]
  3502. (cond
  3503. (= (state/get-current-page) (str (:block/uuid block)))
  3504. nil
  3505. (and parent (:block/parent parent))
  3506. (state/exit-editing-and-set-selected-blocks!
  3507. [(util/get-first-block-by-id (:block/uuid parent))])
  3508. (:block/name parent)
  3509. ;; page block
  3510. (select-all-blocks! {:page (:block/name parent)}))))
  3511. (select-all-blocks! {})))))))
  3512. (defn escape-editing
  3513. [& {:keys [select? save-block?]
  3514. :or {save-block? true}}]
  3515. (let [edit-block (state/get-edit-block)]
  3516. (p/do!
  3517. (when save-block? (save-current-block!))
  3518. (if select?
  3519. (when-let [node (some-> (state/get-input) (util/rec-get-node "ls-block"))]
  3520. (state/exit-editing-and-set-selected-blocks! [node]))
  3521. (when (= (:db/id edit-block) (:db/id (state/get-edit-block)))
  3522. (state/clear-edit!))))))
  3523. (defn replace-block-reference-with-content-at-point
  3524. []
  3525. (let [repo (state/get-current-repo)]
  3526. (when-let [{:keys [start end link]} (thingatpt/block-ref-at-point)]
  3527. (when-let [block (db/entity [:block/uuid link])]
  3528. (let [block-content (:block/title block)
  3529. format (get block :block/format :markdown)
  3530. block-content-without-prop (-> (property-file/remove-properties-when-file-based repo format block-content)
  3531. (drawer/remove-logbook))]
  3532. (when-let [input (state/get-input)]
  3533. (when-let [current-block-content (gobj/get input "value")]
  3534. (let [block-content* (str (subs current-block-content 0 start)
  3535. block-content-without-prop
  3536. (subs current-block-content end))]
  3537. (state/set-block-content-and-last-pos! input block-content* 1)))))))))
  3538. (defn copy-current-ref
  3539. [block-id]
  3540. (when block-id
  3541. (util/copy-to-clipboard! (ref/->block-ref block-id))))
  3542. (defn delete-current-ref!
  3543. [block ref-id]
  3544. (when (and block ref-id)
  3545. (let [content (if (config/db-based-graph?)
  3546. (string/replace (:block/title block) (ref/->page-ref ref-id) "")
  3547. (let [match (re-pattern (str "\\s?"
  3548. (string/replace (ref/->block-ref ref-id) #"([\(\)])" "\\$1")))]
  3549. (string/replace (:block/title block) match "")))]
  3550. (save-block! (state/get-current-repo)
  3551. (:block/uuid block)
  3552. content))))
  3553. (defn replace-ref-with-text!
  3554. [block ref-id]
  3555. (when (and block ref-id)
  3556. (let [repo (state/get-current-repo)
  3557. match (ref/->block-ref ref-id)
  3558. ref-block (db/entity [:block/uuid ref-id])
  3559. block-ref-content (->> (or (:block/title ref-block) "")
  3560. (property-file/remove-built-in-properties-when-file-based repo
  3561. (get ref-block :block/format :markdown))
  3562. (drawer/remove-logbook))
  3563. content (string/replace-first (:block/title block) match
  3564. block-ref-content)]
  3565. (save-block! (state/get-current-repo)
  3566. (:block/uuid block)
  3567. content))))
  3568. (defn replace-ref-with-embed!
  3569. [block ref-id]
  3570. (when (and block ref-id)
  3571. (let [match (ref/->block-ref ref-id)
  3572. content (string/replace-first (:block/title block) match
  3573. (util/format "{{embed ((%s))}}"
  3574. (str ref-id)))]
  3575. (save-block! (state/get-current-repo)
  3576. (:block/uuid block)
  3577. content))))
  3578. (defn block-default-collapsed?
  3579. "Whether a block should be collapsed by default.
  3580. Currently, this handles all the kinds of views."
  3581. [block config]
  3582. (let [block (or (db/entity (:db/id block)) block)]
  3583. (or
  3584. (util/collapsed? block)
  3585. (and (util/mobile?) (ldb/class-instance? (entity-plus/entity-memoized (db/get-db) :logseq.class/Query) block))
  3586. (and (or (:list-view? config) (:ref? config))
  3587. (or (:block/_parent block) (:block.temp/has-children? block))
  3588. (integer? (:block-level config))
  3589. (>= (:block-level config) (state/get-ref-open-blocks-level)))
  3590. (and (or (:view? config) (:popup? config))
  3591. (or (ldb/page? block)
  3592. (:table-block-title? config))))))
  3593. (defn batch-set-heading!
  3594. [block-ids heading]
  3595. (let [repo (state/get-current-repo)]
  3596. (if (config/db-based-graph? repo)
  3597. (db-editor-handler/batch-set-heading! repo block-ids heading)
  3598. (file-editor-handler/batch-set-heading! block-ids heading))))
  3599. (defn set-heading!
  3600. [block-id heading]
  3601. (batch-set-heading! [block-id] heading))
  3602. (defn remove-heading!
  3603. [block-id]
  3604. (set-heading! block-id nil))
  3605. (defn batch-remove-heading!
  3606. [block-ids]
  3607. (batch-set-heading! block-ids nil))
  3608. (defn block->data-transfer!
  3609. "Set block or page name to the given event's dataTransfer. Used in dnd."
  3610. [block-or-page-name event page?]
  3611. (.setData (gobj/get event "dataTransfer")
  3612. (if page? "page-name" "block-uuid")
  3613. (str block-or-page-name)))
  3614. (defn run-query-command!
  3615. []
  3616. (let [repo (state/get-current-repo)]
  3617. (when-let [block (some-> (state/get-edit-block)
  3618. :db/id
  3619. (db/entity))]
  3620. (p/do!
  3621. (save-current-block!)
  3622. (state/clear-edit!)
  3623. (p/let [query-block (or (:logseq.property/query block)
  3624. (p/do!
  3625. (property-handler/set-block-property! repo (:db/id block) :logseq.property/query "")
  3626. (:logseq.property/query (db/entity (:db/id block)))))
  3627. current-query (:block/title (db/entity (:db/id block)))]
  3628. (p/do!
  3629. (ui-outliner-tx/transact!
  3630. {:outliner-op :save-block}
  3631. (property-handler/set-block-property! repo (:db/id block) :block/tags :logseq.class/Query)
  3632. (save-block-inner! block "" {})
  3633. (when query-block
  3634. (save-block-inner! query-block current-query {})))))))))
  3635. (defn show-quick-add
  3636. []
  3637. (let [graph (state/get-current-repo)]
  3638. (p/do!
  3639. (db-async/<get-block graph (date/today))
  3640. (p/let [add-page (db-async/<get-block graph (:db/id (ldb/get-built-in-page (db/get-db) common-config/quick-add-page-name)))
  3641. user-id (when-let [id-str (user-handler/user-uuid)] (uuid id-str))
  3642. user-db-id (when user-id (:db/id (db/entity [:block/uuid user-id])))
  3643. children (:block/_parent add-page)
  3644. children' (if user-db-id
  3645. (filter (fn [block]
  3646. (let [create-by-id (:db/id (:logseq.property/created-by-ref block))]
  3647. (= user-db-id create-by-id))) children)
  3648. children)]
  3649. (when (empty? children')
  3650. (api-insert-new-block! "" {:page (:block/uuid add-page)
  3651. :container-id :unknown-container
  3652. :replace-empty-target? false})))
  3653. (state/pub-event! [(if (util/mobile?)
  3654. :dialog/mobile-quick-add
  3655. :dialog/quick-add)]))))
  3656. (defn quick-add-blocks!
  3657. []
  3658. (let [today (db/get-page (date/today))
  3659. add-page (ldb/get-built-in-page (db/get-db) common-config/quick-add-page-name)]
  3660. (when (and today add-page)
  3661. (let [children (:block/_parent (db/entity (:db/id add-page)))]
  3662. (p/do!
  3663. (when (seq children)
  3664. (if-let [today-last-child (last (ldb/sort-by-order (:block/_parent today)))]
  3665. (move-blocks! children today-last-child {:sibling? true})
  3666. (move-blocks! children today {:sibling? false})))
  3667. (state/close-modal!)
  3668. (mobile-state/set-popup! nil)
  3669. (when (seq children)
  3670. (notification/show! "Blocks added to today!" :success)))))))
  3671. (defn quick-add
  3672. []
  3673. (if (shui-dialog/get-modal :ls-dialog-quick-add)
  3674. (quick-add-blocks!)
  3675. (show-quick-add)))
  3676. (defn quick-add-open-last-block!
  3677. []
  3678. (when-let [add-page (ldb/get-built-in-page (db/get-db) common-config/quick-add-page-name)]
  3679. (prn :debug :add-page add-page
  3680. :children (:block/_parent add-page))
  3681. (when (:block/_parent add-page)
  3682. (let [block (last (ldb/sort-by-order (:block/_parent add-page)))]
  3683. (edit-block! block :max {:container-id :unknown-container})))))