/src/library/slix/ced/doclib.clj

http://github.com/ksuzuki/Sevenri · Clojure · 363 lines · 286 code · 59 blank · 18 comment · 68 complexity · 1073ed40548e0bfb2cdda7ca769d9c59 MD5 · raw file

  1. ;; %! Copyright (C) 2011 Kei Suzuki All rights reserved. !%
  2. ;;
  3. ;; This file is part of Sevenri, a Clojure environment ("This Software").
  4. ;;
  5. ;; The use and distribution terms for this software are covered by the Eclipse
  6. ;; Public License version 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
  7. ;; which can be found in the COPYING at the root of this distribution.
  8. ;; By using this software in any fashion, you are agreeing to be bound by the
  9. ;; terms of this license.
  10. ;; You must not remove this notice, or any other, from this software.
  11. (ns library.slix.ced.doclib
  12. (:use [sevenri log])
  13. (:import (javax.swing.text Segment)))
  14. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  15. (def *dcntxt* nil)
  16. (def *txtseg* nil)
  17. (def *open-close-pairs* {\( [\( \)]
  18. \{ [\{ \}]
  19. \[ [\[ \]]
  20. \) [\) \(]
  21. \} [\} \{]
  22. \] [\] \[]})
  23. (def *open-close-pairs-s* {"(" ["(" ")"]
  24. "{" ["{" "}"]
  25. "[" ["[" "]"]
  26. ")" [")" "("]
  27. "}" ["}" "{"]
  28. "]" ["]" "["]})
  29. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  30. (defn create-doc-context
  31. [ced]
  32. (let [doc (.getDocument ced)
  33. doc-elm (.getDefaultRootElement doc)]
  34. {:ced ced
  35. :doc doc
  36. :pos (.getCaretPosition ced)
  37. :min-pos 0
  38. :max-pos (dec (.getLength doc))
  39. :doc-elm doc-elm
  40. :doc-ect (.getElementCount doc-elm)}))
  41. (defmacro setup-doc-context
  42. [ced dcntxt & body]
  43. `(binding [~'*dcntxt* (merge (create-doc-context ~ced) ~dcntxt)]
  44. ~@body))
  45. (defmacro get-doc-context
  46. ([]
  47. `~'*dcntxt*)
  48. ([kwd]
  49. `(~kwd ~'*dcntxt*)))
  50. (defmacro update-doc-context
  51. [dcntxt & body]
  52. `(binding [~'*dcntxt* (merge (get-doc-context) ~dcntxt)]
  53. ~@body))
  54. (defmacro do-assert
  55. [x]
  56. ;;`(#_(assert ~x)))
  57. `(assert ~x))
  58. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  59. (defmacro partial-segment
  60. ([]
  61. `(.setPartialReturn ~'*txtseg* true))
  62. ([seg]
  63. `(.setPartialReturn ~seg true)))
  64. (defmacro get-partial-txtseg
  65. [beg len]
  66. `(let [seg# (Segment.)]
  67. (partial-segment seg#)
  68. (.getText (get-doc-context :doc) ~beg ~len seg#)
  69. seg#))
  70. (defmacro exact-segment
  71. ([]
  72. `(.setPartialReturn ~'*txtseg* false))
  73. ([seg]
  74. `(.setPartialReturn ~seg false)))
  75. (defmacro get-exact-txtseg
  76. [beg len]
  77. `(let [seg# (Segment.)]
  78. (exact-segment seg#)
  79. (.getText (get-doc-context :doc) ~beg ~len seg#)
  80. seg#))
  81. (defmacro get-txtseg-begin-end
  82. ([]
  83. `(let [seg# ~'*txtseg*]
  84. [(.getBeginIndex seg#) (.getEndIndex seg#)]))
  85. ([seg]
  86. `[(.getBeginIndex ~seg) (.getEndIndex ~seg)]))
  87. (defmacro get-txtseg-begin-len
  88. ([]
  89. `(let [seg# ~'*txtseg*]
  90. [(.getBeginIndex seg#) (.length seg#)]))
  91. ([seg]
  92. `[(.getBeginIndex ~seg) (.length ~seg)]))
  93. (defn get-txtseg
  94. [beg len]
  95. (let [seg (get-partial-txtseg beg len)
  96. [_ slen] (get-txtseg-begin-len seg)]
  97. (if (= len slen)
  98. seg
  99. (let [seg (get-exact-txtseg beg len)]
  100. (do-assert (= len (.length seg)))
  101. #_(lg "exact seg for beg:" beg "len:" len)
  102. seg))))
  103. (defmacro setup-txt-segment
  104. [beg len & body]
  105. `(binding [~'*txtseg* (get-txtseg ~beg ~len)]
  106. ~@body))
  107. (defmacro txtseg-char-at
  108. ([off]
  109. `(.charAt ~'*txtseg* ~off))
  110. ([seg off]
  111. `(.carAt ~seg ~off)))
  112. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  113. (defmacro str-at
  114. ([pos]
  115. `(.getText (get-doc-context :doc) ~pos 1))
  116. ([pos len]
  117. `(.getText (get-doc-context :doc) ~pos ~len)))
  118. (defmacro line-index
  119. [pos]
  120. `(.getElementIndex (get-doc-context :doc-elm) ~pos))
  121. (defmacro line-index-to-start-pos
  122. [lindex]
  123. `(.getStartOffset (.getElement (get-doc-context :doc-elm) ~lindex)))
  124. (defmacro line-index-to-end-pos
  125. [lindex]
  126. `(.getEndOffset (.getElement (get-doc-context :doc-elm) ~lindex)))
  127. (defmacro start-of-line
  128. [pos]
  129. `(.getStartOffset (.getParagraphElement (get-doc-context :doc) ~pos)))
  130. (defmacro end-of-line
  131. [pos]
  132. `(dec (.getEndOffset (.getParagraphElement (get-doc-context :doc) ~pos))))
  133. (defmacro start-and-end-line
  134. [pos]
  135. `(let [e# (.getParagraphElement (get-doc-context :doc) ~pos)]
  136. [(.getStartOffset e#) (.getEndOffset e#)]))
  137. (defmacro start-and-len-line
  138. [pos]
  139. `(let [e# (.getParagraphElement (get-doc-context :doc) ~pos)
  140. s# (.getStartOffset e#)]
  141. [s# (- (.getEndOffset e#) s#)]))
  142. (defmacro pos-to-col
  143. [pos]
  144. `(- ~pos (start-of-line ~pos)))
  145. (defn start-of-prev-line
  146. [pos]
  147. (let [doc-elm (get-doc-context :doc-elm)
  148. idx (.getElementIndex doc-elm pos)]
  149. (when (< (get-doc-context :min-pos) idx)
  150. (.getStartOffset (.getElement doc-elm (dec idx))))))
  151. (defn end-of-prev-line
  152. [pos]
  153. (let [doc-elm (get-doc-context :doc-elm)
  154. idx (.getElementIndex doc-elm pos)]
  155. (when (< (get-doc-context :min-pos) idx)
  156. (dec (.getEndOffset (.getElement doc-elm (dec idx)))))))
  157. (defmacro newline?
  158. [c]
  159. `(= ~c \newline))
  160. (defmacro newline-s?
  161. [s]
  162. `(= ~s "\n"))
  163. (defmacro space?
  164. [c]
  165. `(or (= ~c \space) (= ~c \tab) (= ~c \newline)))
  166. (defmacro space-s?
  167. [s]
  168. `(or (= ~s " ") (= ~s "\t") (= ~s "\n")))
  169. (defmacro escape?
  170. [c]
  171. `(= ~c \\))
  172. (defmacro escape-s?
  173. [s]
  174. `(= ~s "\\"))
  175. (defmacro comment?
  176. [c]
  177. `(= ~c \;))
  178. (defmacro comment-s?
  179. [s]
  180. `(= ~s ";"))
  181. (defmacro dquote?
  182. [c]
  183. `(= ~c \"))
  184. (defmacro dquote-s?
  185. [s]
  186. `(= ~s "\""))
  187. (defmacro opening?
  188. [c]
  189. `(or (= ~c \() (= ~c \{) (= ~c \[)))
  190. (defmacro opening-s?
  191. [s]
  192. `(or (= ~s "(") (= ~s "{") (= ~s "[")))
  193. (defmacro closing?
  194. [c]
  195. `(or (= ~c \]) (= ~c \}) (= ~c \))))
  196. (defmacro closing-s?
  197. [s]
  198. `(or (= ~s "]") (= ~s "}") (= ~s ")")))
  199. (defmacro opening-or-closing?
  200. [c]
  201. `(get *open-close-pairs* ~c))
  202. (defmacro opening-or-closing-s?
  203. [s]
  204. `(get *open-close-pairs-s* ~s))
  205. (defmacro matching-paren-of
  206. [c]
  207. `(second (get *open-close-pairs* ~c)))
  208. (defmacro matching-paren-of-s
  209. [s]
  210. `(second (get *open-close-pairs-s* ~s)))
  211. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  212. (defn first-char
  213. "Returns [pos beg seg] or nil"
  214. ([pos]
  215. (let [[s l] (start-and-len-line pos)]
  216. (first-char (get-txtseg s l) s 0 l)))
  217. ([seg beg off len]
  218. (when (< off len)
  219. (if (space? (.charAt seg off))
  220. (recur seg beg (inc off) len)
  221. [(+ beg off) beg seg]))))
  222. (defn last-char
  223. "Returns [pos beg seg first] or nil"
  224. [pos]
  225. (when-let [a (first-char pos)]
  226. (let [[first beg seg] a
  227. len (.length seg)]
  228. (loop [lof (- first beg)
  229. off lof]
  230. (if (= off len)
  231. [(+ beg lof) beg seg first]
  232. (recur (if (space? (.charAt seg off)) lof off) (inc off)))))))
  233. (defn last-code-char
  234. "Returns [pos beg seg first] or nil"
  235. [pos]
  236. (when-let [a (first-char pos)]
  237. (let [[first beg seg] a
  238. len (.length seg)]
  239. (loop [lof nil
  240. off (- first beg)
  241. lc 0
  242. ins? false]
  243. (if (= off len)
  244. (when lof
  245. [(+ beg lof) beg seg first])
  246. (let [c (.charAt seg off)]
  247. (if ins?
  248. (if (dquote? c)
  249. (recur off (inc off) c (if (escape? lc) true false))
  250. (recur off (inc off) c true))
  251. (if (and (comment? c)
  252. (or (zero? off)
  253. (and (pos? off) (not (escape? (.charAt seg (dec off)))))))
  254. (when lof
  255. [(+ beg lof) beg seg first])
  256. (if (dquote? c)
  257. (recur off (inc off) c (if (escape? lc) false true))
  258. (recur (if (space? c) lof off) (inc off) c false))))))))))
  259. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  260. (defn top-level-form
  261. "Return pos or nil. All top level forms should start at the first column
  262. after a blank line, except at the beginning of the document."
  263. [pos]
  264. (letfn [(abl [p]
  265. (if (= p (get-doc-context :min-pos))
  266. p
  267. (when-not (last-code-char (dec p))
  268. p)))
  269. (tlf [p l]
  270. (let [s (str-at p)]
  271. (if (= s "(")
  272. (abl p)
  273. (when (and (= s "#")
  274. (re-matches #"^(#_\s*)?\(.*\n$" (str-at p l)))
  275. (abl p)))))]
  276. (if-let [p (apply tlf (start-and-len-line pos))]
  277. p
  278. (loop [pos (start-of-prev-line pos)]
  279. (when pos
  280. (if-let [p (apply tlf (start-and-len-line pos))]
  281. p
  282. (recur (start-of-prev-line pos))))))))
  283. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  284. (defn fetch-symbol
  285. ([ced]
  286. (setup-doc-context ced nil
  287. (let [pos (get-doc-context :pos)
  288. [s l] (start-and-len-line pos)
  289. off (- pos s)]
  290. (setup-txt-segment s l
  291. (if (space? (txtseg-char-at off))
  292. (when (pos? off)
  293. (when-not (space? (txtseg-char-at (dec off)))
  294. (fetch-symbol ced s l (dec off))))
  295. (fetch-symbol ced s l off))))))
  296. ([ced start len off]
  297. (loop [off off]
  298. (if (and (pos? off)
  299. (not (space? (txtseg-char-at off))))
  300. (recur (dec off))
  301. (when-let [match (re-matches #"[^\w\*\+\!\-\_\?\.]*([\w\*\+\!\-\_\?\.\$]+).*\n$"
  302. (.toString (.subSequence *txtseg* off len)))]
  303. #_(lg "fetched symbol:" (second match))
  304. (second match))))))