Helios-Core.st 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. Smalltalk current createPackage: 'Helios-Core' properties: #{}!
  2. Object subclass: #HLEnvironment
  3. instanceVariableNames: ''
  4. package: 'Helios-Core'!
  5. !HLEnvironment commentStamp!
  6. Abstract class defining common behavior for local and remote environments!
  7. !HLEnvironment methodsFor: 'accessing'!
  8. packages
  9. ^ self subclassResponsibility
  10. ! !
  11. !HLEnvironment methodsFor: 'actions'!
  12. eval: someCode on: aReceiver
  13. ^ self subclassResponsibility
  14. ! !
  15. HLEnvironment subclass: #HLLocalEnvironment
  16. instanceVariableNames: ''
  17. package: 'Helios-Core'!
  18. !HLLocalEnvironment methodsFor: 'accessing'!
  19. packages
  20. ^ Smalltalk current packages
  21. ! !
  22. !HLLocalEnvironment methodsFor: 'actions'!
  23. eval: someCode on: aReceiver
  24. | compiler |
  25. compiler := Compiler new.
  26. [compiler parseExpression: someCode] on: Error do: [:ex |
  27. ^window alert: ex messageText].
  28. ^(compiler eval: (compiler compile: 'doIt ^[', someCode, '] value' forClass: DoIt)) fn applyTo: aReceiver arguments: #()
  29. ! !
  30. HLEnvironment subclass: #HLRemoteEnvironment
  31. instanceVariableNames: ''
  32. package: 'Helios-Core'!
  33. !HLRemoteEnvironment methodsFor: 'accessing'!
  34. packages
  35. "Answer the remote environment's packages"
  36. "to-do"
  37. "Note for future self and friends:
  38. the problem with remote stuff is that the answers shouldn't be expected to be
  39. received in a syncrhonous fashion. Everything network is asyc, so you *are going to deal with callbacks* here"
  40. ! !
  41. !HLRemoteEnvironment methodsFor: 'actions'!
  42. eval: someCode on: aReceiver
  43. "Note for future self and friends:
  44. whatever way this compilation happens on the other side,
  45. it should return a proxy to the remote resulting object"
  46. self notYetImplemented
  47. ! !
  48. Object subclass: #HLRemoteObject
  49. instanceVariableNames: ''
  50. package: 'Helios-Core'!
  51. !HLRemoteObject commentStamp!
  52. This is a local proxy to a remote object.
  53. Tipically useful for evaluating and inspecting and interacting with instances of a remote VM.!
  54. !HLRemoteObject methodsFor: 'actions'!
  55. doesNotUnderstand: aMessage
  56. "to-do
  57. aham, blah blah
  58. super doesNotUnderstand: aMessage"
  59. !
  60. inspectOn: anInspector
  61. "to-do"
  62. "this is a source of so much fun..."
  63. !
  64. printString
  65. ^ 'this is a remote object'
  66. ! !
  67. Widget subclass: #HLSourceArea
  68. instanceVariableNames: 'editor textarea div receiver onDoIt'
  69. package: 'Helios-Core'!
  70. !HLSourceArea methodsFor: 'accessing'!
  71. contents
  72. ^editor getValue
  73. !
  74. contents: aString
  75. editor setValue: aString
  76. !
  77. currentLine
  78. ^editor getLine: (editor getCursor line)
  79. !
  80. currentLineOrSelection
  81. ^editor somethingSelected
  82. ifFalse: [self currentLine]
  83. ifTrue: [self selection]
  84. !
  85. editor
  86. ^editor
  87. !
  88. onDoIt
  89. ^onDoIt
  90. !
  91. onDoIt: aBlock
  92. onDoIt := aBlock
  93. !
  94. receiver
  95. ^receiver ifNil: [DoIt new]
  96. !
  97. receiver: anObject
  98. receiver := anObject
  99. !
  100. selection
  101. ^editor getSelection
  102. !
  103. selectionEnd
  104. ^textarea element selectionEnd
  105. !
  106. selectionEnd: anInteger
  107. textarea element selectionEnd: anInteger
  108. !
  109. selectionStart
  110. ^textarea element selectionStart
  111. !
  112. selectionStart: anInteger
  113. textarea element selectionStart: anInteger
  114. !
  115. setEditorOn: aTextarea
  116. <self['@editor'] = CodeMirror.fromTextArea(aTextarea, {
  117. theme: 'amber',
  118. lineNumbers: true,
  119. enterMode: 'flat',
  120. matchBrackets: true,
  121. electricChars: false
  122. })>
  123. !
  124. val
  125. ^editor getValue
  126. !
  127. val: aString
  128. editor setValue: aString
  129. ! !
  130. !HLSourceArea methodsFor: 'actions'!
  131. clear
  132. self contents: ''
  133. !
  134. doIt
  135. | result |
  136. result := self eval: self currentLineOrSelection.
  137. self onDoIt ifNotNil: [self onDoIt value].
  138. ^result
  139. !
  140. eval: aString
  141. | compiler |
  142. compiler := Compiler new.
  143. [compiler parseExpression: aString] on: Error do: [:ex |
  144. ^window alert: ex messageText].
  145. ^(compiler eval: (compiler compile: 'doIt ^[', aString, '] value' forClass: DoIt)) fn applyTo: self receiver arguments: #()
  146. !
  147. fileIn
  148. Importer new import: self currentLineOrSelection readStream
  149. !
  150. handleKeyDown: anEvent
  151. <if(anEvent.ctrlKey) {
  152. if(anEvent.keyCode === 80) { //ctrl+p
  153. self._printIt();
  154. anEvent.preventDefault();
  155. return false;
  156. }
  157. if(anEvent.keyCode === 68) { //ctrl+d
  158. self._doIt();
  159. anEvent.preventDefault();
  160. return false;
  161. }
  162. if(anEvent.keyCode === 73) { //ctrl+i
  163. self._inspectIt();
  164. anEvent.preventDefault();
  165. return false;
  166. }
  167. }>
  168. !
  169. inspectIt
  170. self doIt inspect
  171. !
  172. print: aString
  173. | start stop |
  174. start := HashedCollection new.
  175. stop := HashedCollection new.
  176. start at: 'line' put: (editor getCursor: false) line.
  177. start at: 'ch' put: (editor getCursor: false) ch.
  178. stop at: 'line' put: (start at: 'line').
  179. stop at: 'ch' put: ((start at: 'ch') + aString size + 2).
  180. editor replaceSelection: (editor getSelection, ' ', aString, ' ').
  181. editor setCursor: (editor getCursor: true).
  182. editor setSelection: stop end: start
  183. !
  184. printIt
  185. self print: self doIt printString
  186. ! !
  187. !HLSourceArea methodsFor: 'events'!
  188. onKeyDown: aBlock
  189. div onKeyDown: aBlock
  190. !
  191. onKeyUp: aBlock
  192. div onKeyUp: aBlock
  193. ! !
  194. !HLSourceArea methodsFor: 'rendering'!
  195. renderOn: html
  196. div := html div class: 'source'.
  197. div with: [textarea := html textarea].
  198. self setEditorOn: textarea element.
  199. div onKeyDown: [:e | self handleKeyDown: e]
  200. ! !
  201. Object subclass: #HLTab
  202. instanceVariableNames: 'widget label'
  203. package: 'Helios-Core'!
  204. !HLTab methodsFor: 'accessing'!
  205. activate
  206. self manager activate: self
  207. !
  208. add
  209. self manager addTab: self
  210. !
  211. label
  212. ^ label ifNil: [ '' ]
  213. !
  214. label: aString
  215. label := aString
  216. !
  217. manager
  218. ^ HLManager current
  219. !
  220. widget
  221. ^ widget
  222. !
  223. widget: aWidget
  224. widget := aWidget
  225. ! !
  226. !HLTab methodsFor: 'testing'!
  227. isActive
  228. ^ self manager activeTab = self
  229. ! !
  230. !HLTab class methodsFor: 'instance creation'!
  231. on: aWidget labelled: aString
  232. ^ self new
  233. widget: aWidget;
  234. label: aString;
  235. yourself
  236. ! !
  237. Widget subclass: #HLWidget
  238. instanceVariableNames: 'rootDiv'
  239. package: 'Helios-Core'!
  240. !HLWidget methodsFor: 'accessing'!
  241. manager
  242. ^ HLManager current
  243. ! !
  244. !HLWidget methodsFor: 'announcements'!
  245. subscribeTo: anAnnouncer
  246. ! !
  247. !HLWidget methodsFor: 'keybindings'!
  248. registerBindings
  249. self registerBindingsOn: self manager keyBinder bindings
  250. !
  251. registerBindingsOn: aBindingGroup
  252. ! !
  253. !HLWidget methodsFor: 'rendering'!
  254. renderContentOn: html
  255. !
  256. renderOn: html
  257. self registerBindings.
  258. rootDiv := html div with: [
  259. self renderContentOn: html ]
  260. ! !
  261. !HLWidget methodsFor: 'updating'!
  262. refresh
  263. rootDiv ifNil: [ ^ self ].
  264. rootDiv asJQuery empty.
  265. [ :html | self renderContentOn: html ] appendToJQuery: rootDiv asJQuery
  266. ! !
  267. !HLWidget class methodsFor: 'accessing'!
  268. openAsTab
  269. HLManager current addTab: (HLTab on: self new labelled: self tabLabel)
  270. !
  271. tabLabel
  272. ^ 'Tab'
  273. !
  274. tabPriority
  275. ^ 500
  276. ! !
  277. !HLWidget class methodsFor: 'testing'!
  278. canBeOpenAsTab
  279. ^ false
  280. ! !
  281. HLWidget subclass: #HLDebugger
  282. instanceVariableNames: ''
  283. package: 'Helios-Core'!
  284. HLWidget subclass: #HLFocusableWidget
  285. instanceVariableNames: 'hiddenInput'
  286. package: 'Helios-Core'!
  287. !HLFocusableWidget methodsFor: 'accessing'!
  288. focusClass
  289. ^ 'focused'
  290. ! !
  291. !HLFocusableWidget methodsFor: 'events'!
  292. blur
  293. hiddenInput asJQuery blur
  294. !
  295. focus
  296. hiddenInput asJQuery focus
  297. !
  298. hasFocus
  299. ^ rootDiv notNil and: [ rootDiv asJQuery hasClass: self focusClass ]
  300. ! !
  301. !HLFocusableWidget methodsFor: 'rendering'!
  302. renderContentOn: html
  303. !
  304. renderHiddenInputOn: html
  305. hiddenInput := html input
  306. style: 'position: absolute; left: -100000px;';
  307. onBlur: [ rootDiv asJQuery removeClass: self focusClass ];
  308. onFocus: [ rootDiv asJQuery addClass: self focusClass ]
  309. !
  310. renderOn: html
  311. self registerBindings.
  312. self renderHiddenInputOn: html.
  313. rootDiv := html div
  314. class: 'hl_widget';
  315. onClick: [ hiddenInput asJQuery focus ];
  316. with: [
  317. self renderContentOn: html ]
  318. ! !
  319. HLFocusableWidget subclass: #HLListWidget
  320. instanceVariableNames: 'items selectedItem'
  321. package: 'Helios-Core'!
  322. !HLListWidget methodsFor: 'accessing'!
  323. cssClassForItem: anObject
  324. ^ self selectedItem = anObject
  325. ifTrue: [ 'active' ]
  326. ifFalse: [ 'inactive' ]
  327. !
  328. iconForItem: anObject
  329. ^ ''
  330. !
  331. items
  332. ^ items ifNil: [ #() ]
  333. !
  334. items: aCollection
  335. items := aCollection
  336. !
  337. selectedItem
  338. ^ selectedItem
  339. !
  340. selectedItem: anObject
  341. selectedItem := anObject
  342. ! !
  343. !HLListWidget methodsFor: 'actions'!
  344. activateListItem: aListItem
  345. | parent position |
  346. (aListItem get: 0) ifNil: [ ^self ].
  347. <position = aListItem.parent().children().get().indexOf(aListItem.get(0)) + 1>.
  348. parent := aListItem parent.
  349. parent children removeClass: 'active'.
  350. aListItem addClass: 'active'.
  351. "Move the scrollbar to show the active element"
  352. aListItem position top < 0 ifTrue: [
  353. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem position top - 10) ].
  354. aListItem position top + aListItem height > parent height ifTrue: [
  355. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem height - (parent height - aListItem position top)) +10 ].
  356. "Activate the corresponding item"
  357. self selectItem: (self items at: (aListItem attr: 'list-data') asNumber)
  358. !
  359. selectItem: anObject
  360. self selectedItem: anObject
  361. ! !
  362. !HLListWidget methodsFor: 'events'!
  363. setupKeyBindings
  364. | next |
  365. hiddenInput asJQuery unbind: 'keydown'.
  366. hiddenInput asJQuery keydown: [ :e | | selected |
  367. selected := window jQuery: '.focused .nav-pills .active'.
  368. e which = 38 ifTrue: [
  369. self activateListItem: selected prev ].
  370. e which = 40 ifTrue: [
  371. next := selected next.
  372. (next get: 0) ifNil: [ next := window jQuery: '.focused .nav-pills li:first-child' ].
  373. self activateListItem: next ] ]
  374. ! !
  375. !HLListWidget methodsFor: 'rendering'!
  376. renderButtonsOn: html
  377. !
  378. renderContentOn: html
  379. html ul
  380. class: 'nav nav-pills nav-stacked';
  381. with: [ self renderListOn: html ].
  382. html div class: 'pane_actions form-actions'; with: [
  383. self renderButtonsOn: html ].
  384. self setupKeyBindings
  385. !
  386. renderItem: anObject on: html
  387. | li |
  388. li := html li.
  389. li
  390. class: (self cssClassForItem: anObject);
  391. at: 'list-data' put: (self items indexOf: anObject) asString;
  392. with: [
  393. html a
  394. with: [
  395. (html tag: 'i') class: (self iconForItem: anObject).
  396. self renderItemLabel: anObject on: html ];
  397. onClick: [
  398. self activateListItem: li asJQuery ] ]
  399. !
  400. renderItemLabel: anObject on: html
  401. html with: anObject asString
  402. !
  403. renderListOn: html
  404. self items do: [ :each |
  405. self renderItem: each on: html ]
  406. ! !
  407. HLListWidget subclass: #HLNavigationListWidget
  408. instanceVariableNames: 'previous next'
  409. package: 'Helios-Core'!
  410. !HLNavigationListWidget methodsFor: 'accessing'!
  411. next
  412. ^ next
  413. !
  414. next: aWidget
  415. next := aWidget.
  416. aWidget previous = self ifFalse: [ aWidget previous: self ]
  417. !
  418. previous
  419. ^ previous
  420. !
  421. previous: aWidget
  422. previous := aWidget.
  423. aWidget next = self ifFalse: [ aWidget next: self ]
  424. ! !
  425. !HLNavigationListWidget methodsFor: 'actions'!
  426. nextFocus
  427. self next ifNotNil: [ self next focus ]
  428. !
  429. previousFocus
  430. self previous ifNotNil: [ self previous focus ]
  431. ! !
  432. !HLNavigationListWidget methodsFor: 'events'!
  433. setupKeyBindings
  434. super setupKeyBindings.
  435. hiddenInput asJQuery keydown: [ :e |
  436. e which = 39 ifTrue: [
  437. self nextFocus ].
  438. e which = 37 ifTrue: [
  439. self previousFocus ] ]
  440. ! !
  441. HLWidget subclass: #HLManager
  442. instanceVariableNames: 'tabs activeTab keyBinder'
  443. package: 'Helios-Core'!
  444. !HLManager methodsFor: 'accessing'!
  445. activeTab
  446. ^ activeTab
  447. !
  448. keyBinder
  449. ^ keyBinder ifNil: [ keyBinder := HLKeyBinder new ]
  450. !
  451. tabs
  452. ^ tabs ifNil: [ tabs := OrderedCollection new ]
  453. ! !
  454. !HLManager methodsFor: 'actions'!
  455. activate: aTab
  456. self keyBinder flushBindings.
  457. activeTab := aTab.
  458. self
  459. refresh;
  460. show: aTab
  461. !
  462. addTab: aTab
  463. self tabs add: aTab.
  464. self activate: aTab
  465. !
  466. removeTab: aTab
  467. "Todo: activate the previously activated tab. Keep a history of tabs selection"
  468. (self tabs includes: aTab) ifFalse: [ ^ self ].
  469. self tabs remove: aTab.
  470. self refresh
  471. ! !
  472. !HLManager methodsFor: 'initialization'!
  473. initialize
  474. super initialize.
  475. self keyBinder setupEvents
  476. ! !
  477. !HLManager methodsFor: 'rendering'!
  478. refresh
  479. (window jQuery: '.navbar') remove.
  480. (window jQuery: '#container') remove.
  481. self appendToJQuery: 'body' asJQuery
  482. !
  483. renderAddOn: html
  484. html li
  485. class: 'dropdown';
  486. with: [
  487. html a
  488. class: 'dropdown-toggle';
  489. at: 'data-toggle' put: 'dropdown';
  490. with: [
  491. html with: 'Open...'.
  492. (html tag: 'b') class: 'caret' ].
  493. html ul
  494. class: 'dropdown-menu';
  495. with: [
  496. ((HLWidget withAllSubclasses
  497. select: [ :each | each canBeOpenAsTab ])
  498. sorted: [ :a :b | a tabPriority < b tabPriority ])
  499. do: [ :each |
  500. html li with: [
  501. html a
  502. with: each tabLabel;
  503. onClick: [ each openAsTab ] ] ] ] ]
  504. !
  505. renderContentOn: html
  506. html div
  507. class: 'navbar navbar-fixed-top';
  508. with: [ html div
  509. class: 'navbar-inner';
  510. with: [ self renderTabsOn: html ] ].
  511. html div id: 'container'
  512. !
  513. renderTabsOn: html
  514. html ul
  515. class: 'nav';
  516. with: [
  517. self tabs do: [ :each |
  518. html li
  519. class: (each isActive ifTrue: [ 'active' ] ifFalse: [ 'inactive' ]);
  520. with: [
  521. html a
  522. with: [
  523. ((html tag: 'i') class: 'icon-remove-circle')
  524. onClick: [ self removeTab: each ].
  525. html with: each label ];
  526. onClick: [ each activate ] ] ].
  527. self renderAddOn: html ]
  528. !
  529. show: aTab
  530. (window jQuery: '#container') empty.
  531. aTab widget appendToJQuery: '#container' asJQuery
  532. ! !
  533. HLManager class instanceVariableNames: 'current'!
  534. !HLManager class methodsFor: 'accessing'!
  535. current
  536. ^ current ifNil: [ current := self basicNew initialize ]
  537. ! !
  538. !HLManager class methodsFor: 'initialization'!
  539. initialize
  540. self current appendToJQuery: 'body' asJQuery
  541. ! !
  542. !HLManager class methodsFor: 'instance creation'!
  543. new
  544. "Use current instead"
  545. self shouldNotImplement
  546. ! !
  547. HLWidget subclass: #HLSUnit
  548. instanceVariableNames: ''
  549. package: 'Helios-Core'!
  550. !HLSUnit class methodsFor: 'accessing'!
  551. tabLabel
  552. ^ 'SUnit'
  553. !
  554. tabPriority
  555. ^ 1000
  556. ! !
  557. !HLSUnit class methodsFor: 'testing'!
  558. canBeOpenAsTab
  559. ^ true
  560. ! !
  561. HLWidget subclass: #HLTranscript
  562. instanceVariableNames: ''
  563. package: 'Helios-Core'!
  564. !HLTranscript class methodsFor: 'accessing'!
  565. tabLabel
  566. ^ 'Transcript'
  567. !
  568. tabPriority
  569. ^ 600
  570. ! !
  571. !HLTranscript class methodsFor: 'testing'!
  572. canBeOpenAsTab
  573. ^ true
  574. ! !