ide.st 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. Widget subclass: #TabManager
  2. instanceVariableNames: 'selectedTab tabs opened'
  3. category: 'IDE'!
  4. TabManager class instanceVariableNames: 'current'!
  5. !TabManager class methodsFor: 'instance creation'!
  6. current
  7. ^current ifNil: [current := super new]
  8. !
  9. new
  10. self shouldNotImplement
  11. ! !
  12. !TabManager methodsFor: 'initialization'!
  13. initialize
  14. super initialize.
  15. opened := true.
  16. 'body' asJQuery
  17. append: self;
  18. append: [:html | html div id: 'jtalk'];
  19. addClass: 'jtalkBody'.
  20. self
  21. addTab: Transcript current;
  22. addTab: Workspace new.
  23. self selectTab: self tabs last.
  24. self
  25. onResize: [self updateBodyMargin];
  26. updatePositionOnWindowResize
  27. ! !
  28. !TabManager methodsFor: 'accessing'!
  29. tabs
  30. ^tabs ifNil: [tabs := Array new]
  31. ! !
  32. !TabManager methodsFor: 'adding/Removing'!
  33. addTab: aWidget
  34. self tabs add: aWidget.
  35. '#jtalk' asJQuery append: aWidget.
  36. aWidget root asJQuery hide
  37. !
  38. removeTab: aWidget
  39. self tabs remove: aWidget.
  40. self update
  41. ! !
  42. !TabManager methodsFor: 'actions'!
  43. updateBodyMargin
  44. self setBodyMargin: '#jtalk' asJQuery height + 27
  45. !
  46. removeBodyMargin
  47. self setBodyMargin: 0
  48. !
  49. setBodyMargin: anInteger
  50. '.jtalkBody' asJQuery cssAt: 'margin-bottom' put: anInteger asString, 'px'
  51. !
  52. onResize: aBlock
  53. {'jQuery(''#jtalk'').resizable({handles: ''n'', stop: aBlock, minHeight: 230});'}
  54. !
  55. updatePositionOnWindowResize
  56. {'jQuery(window).resize(function(e){jQuery(''#jtalk'').css(''top'', '''''').css(''bottom'', ''27px'')});'}
  57. !
  58. open
  59. opened ifFalse: [
  60. self root asJQuery show.
  61. 'body' asJQuery addClass: 'jtalkBody'.
  62. '#jtalk' asJQuery show.
  63. self updateBodyMargin.
  64. selectedTab root asJQuery show.
  65. opened := true]
  66. !
  67. close
  68. opened ifTrue: [
  69. self root asJQuery hide.
  70. '#jtalk' asJQuery hide.
  71. self removeBodyMargin.
  72. 'body' asJQuery removeClass: 'jtalkBody'.
  73. opened := false]
  74. !
  75. newBrowserTab
  76. Browser open
  77. !
  78. selectTab: aWidget
  79. self open.
  80. selectedTab := aWidget.
  81. self tabs do: [:each |
  82. each root asJQuery hide].
  83. aWidget root asJQuery show.
  84. self update
  85. !
  86. closeTab: aWidget
  87. self removeTab: aWidget.
  88. self selectTab: self tabs last.
  89. aWidget root asJQuery remove.
  90. self update
  91. ! !
  92. !TabManager methodsFor: 'rendering'!
  93. renderOn: html
  94. html ul
  95. id: 'jtalkTabs';
  96. with: [
  97. html li
  98. class: 'closeAll';
  99. with: 'x';
  100. onClick: [self close].
  101. self tabs do: [:each |
  102. self renderTabFor: each on: html].
  103. html li
  104. class: 'newtab';
  105. with: ' + ';
  106. onClick: [self newBrowserTab]]
  107. !
  108. renderTabFor: aWidget on: html
  109. | li |
  110. li := html li.
  111. selectedTab = aWidget ifTrue: [
  112. li class: 'selected'].
  113. li with: [
  114. html span
  115. with: aWidget label;
  116. onClick: [self selectTab: aWidget].
  117. aWidget canBeClosed ifTrue: [
  118. html span
  119. class: 'close';
  120. with: 'x';
  121. onClick: [self closeTab: aWidget]]]
  122. ! !
  123. Widget subclass: #TabWidget
  124. instanceVariableNames: ''
  125. category: 'IDE'!
  126. !TabWidget class methodsFor: 'instance creation'!
  127. open
  128. ^self new open
  129. ! !
  130. !TabWidget methodsFor: 'accessing'!
  131. label
  132. self subclassResponsibility
  133. ! !
  134. !TabWidget methodsFor: 'actions'!
  135. open
  136. TabManager current
  137. addTab: self;
  138. selectTab: self
  139. ! !
  140. !TabWidget methodsFor: 'testing'!
  141. canBeClosed
  142. ^false
  143. ! !
  144. !TabWidget methodsFor: 'rendering'!
  145. renderOn: html
  146. html root
  147. class: 'jtalkTool';
  148. with: [
  149. html div
  150. class: 'jt_box';
  151. with: [self renderBoxOn: html].
  152. html div
  153. class: 'jt_buttons';
  154. with: [self renderButtonsOn: html]]
  155. !
  156. renderBoxOn: html
  157. !
  158. renderButtonsOn: html
  159. ! !
  160. TabWidget subclass: #Workspace
  161. instanceVariableNames: 'textarea'
  162. category: 'IDE'!
  163. !Workspace methodsFor: 'accessing'!
  164. label
  165. ^'[Workspace]'
  166. !
  167. selection
  168. ^{'return document.selection'}
  169. !
  170. selectionStart
  171. ^{'return jQuery(''.jt_workspace'')[0].selectionStart'}
  172. !
  173. selectionEnd
  174. ^{'return jQuery(''.jt_workspace'')[0].selectionEnd'}
  175. !
  176. selectionStart: anInteger
  177. {'jQuery(''.jt_workspace'')[0].selectionStart = anInteger'}
  178. !
  179. selectionEnd: anInteger
  180. {'jQuery(''.jt_workspace'')[0].selectionEnd = anInteger'}
  181. !
  182. currentLine
  183. | lines startLine endLine|
  184. lines := textarea asJQuery val tokenize: String cr.
  185. startLine := endLine := 0.
  186. lines do: [:each |
  187. endLine := startLine + each size.
  188. startLine := endLine + 1.
  189. endLine >= self selectionStart ifTrue: [
  190. self selectionEnd: endLine.
  191. ^each]]
  192. ! !
  193. !Workspace methodsFor: 'actions'!
  194. handleKeyDown: anEvent
  195. ^{'if(anEvent.ctrlKey) {
  196. if(anEvent.keyCode === 68) { //ctrl+p
  197. self._printIt();
  198. return false;
  199. }
  200. if(anEvent.keyCode === 80) { //ctrl+d
  201. self._doIt();
  202. return false;
  203. }
  204. }'}
  205. !
  206. clearWorkspace
  207. textarea asJQuery val: ''
  208. !
  209. doIt
  210. self printIt
  211. !
  212. printIt
  213. | selection |
  214. textarea asJQuery focus.
  215. self selectionStart = self selectionEnd
  216. ifTrue: [selection := self currentLine]
  217. ifFalse: [
  218. selection := textarea asJQuery val copyFrom: self selectionStart + 1 to: self selectionEnd + 1].
  219. self print: (self eval: selection) printString
  220. !
  221. print: aString
  222. | start |
  223. start := self selectionEnd.
  224. textarea asJQuery val: (
  225. (textarea asJQuery val copyFrom: 1 to: start),
  226. ' ', aString, ' ',
  227. (textarea asJQuery val copyFrom: start + 1 to: textarea asJQuery val size)).
  228. self selectionStart: start.
  229. self selectionEnd: start + aString size + 2
  230. !
  231. eval: aString
  232. ^Compiler new loadExpression: aString
  233. ! !
  234. !Workspace methodsFor: 'rendering'!
  235. renderBoxOn: html
  236. textarea := html textarea.
  237. textarea asJQuery call: 'tabby'.
  238. textarea onKeyDown: [:e | self handleKeyDown: e].
  239. textarea
  240. class: 'jt_workspace';
  241. at: 'spellcheck' put: 'false'
  242. !
  243. renderButtonsOn: html
  244. html button
  245. with: 'DoIt';
  246. title: 'ctrl+d';
  247. onClick: [self doIt].
  248. html button
  249. with: 'PrintIt';
  250. title: 'ctrl+p';
  251. onClick: [self printIt].
  252. html button
  253. with: 'Clear workspace';
  254. onClick: [self clearWorkspace]
  255. ! !
  256. TabWidget subclass: #Transcript
  257. instanceVariableNames: 'textarea'
  258. category: 'IDE'!
  259. Transcript class instanceVariableNames: 'current'!
  260. !Transcript class methodsFor: 'instance creation'!
  261. open
  262. self current open
  263. !
  264. new
  265. self shouldNotImplement
  266. !
  267. current
  268. ^current ifNil: [current := super new]
  269. ! !
  270. !Transcript class methodsFor: 'printing'!
  271. show: anObject
  272. self current show: anObject
  273. !
  274. cr
  275. self current show: String cr
  276. !
  277. clear
  278. self current clear
  279. ! !
  280. !Transcript methodsFor: 'accessing'!
  281. label
  282. ^'[Transcript]'
  283. ! !
  284. !Transcript methodsFor: 'actions'!
  285. show: anObject
  286. textarea asJQuery val: textarea asJQuery val, anObject asString.
  287. !
  288. cr
  289. textarea asJQuery val: textarea asJQuery val, String cr.
  290. !
  291. clear
  292. textarea asJQuery val: ''
  293. ! !
  294. !Transcript methodsFor: 'rendering'!
  295. renderBoxOn: html
  296. textarea := html textarea.
  297. textarea asJQuery call: 'tabby'.
  298. textarea
  299. class: 'jt_transcript';
  300. at: 'spellcheck' put: 'false'
  301. !
  302. renderButtonsOn: html
  303. html button
  304. with: 'Clear transcript';
  305. onClick: [self clear]
  306. ! !
  307. TabWidget subclass: #Browser
  308. instanceVariableNames: 'selectedCategory selectedClass selectedProtocol selectedMethod categoriesList classesList protocolsList methodsList sourceTextarea tabsList selectedTab saveButton classButtons methodButtons'
  309. category: 'IDE'!
  310. !Browser class methodsFor: 'convenience'!
  311. openOn: aClass
  312. self new
  313. open;
  314. selectCategory: aClass category;
  315. selectClass: aClass
  316. !
  317. open
  318. self new open
  319. ! !
  320. !Browser methodsFor: 'initialization'!
  321. initialize
  322. super initialize.
  323. selectedTab := #instance
  324. ! !
  325. !Browser methodsFor: 'accessing'!
  326. label
  327. ^selectedClass
  328. ifNil: ['Browser (nil)']
  329. ifNotNil: [selectedClass name]
  330. !
  331. categories
  332. | categories |
  333. categories := Array new.
  334. Smalltalk current classes do: [:each |
  335. (categories includes: each category) ifFalse: [
  336. categories add: each category]].
  337. ^categories sort
  338. !
  339. classes
  340. ^(Smalltalk current classes
  341. select: [:each | each category = selectedCategory])
  342. sort: [:a :b | a name > b name]
  343. !
  344. protocols
  345. | class protocols |
  346. protocols := Array new.
  347. selectedClass ifNotNil: [
  348. selectedTab = #comment ifTrue: [^#()].
  349. class := selectedTab = #instance
  350. ifTrue: [selectedClass]
  351. ifFalse: [selectedClass class].
  352. class methodDictionary isEmpty ifTrue: [
  353. protocols add: 'not yet classified'].
  354. class methodDictionary do: [:each |
  355. (protocols includes: each category) ifFalse: [
  356. protocols add: each category]]].
  357. ^protocols sort
  358. !
  359. methods
  360. | class |
  361. selectedTab = #comment ifTrue: [^#()].
  362. selectedClass ifNotNil: [
  363. class := selectedTab = #instance
  364. ifTrue: [selectedClass]
  365. ifFalse: [selectedClass class]].
  366. ^(selectedProtocol
  367. ifNil: [
  368. class
  369. ifNil: [#()]
  370. ifNotNil: [class methodDictionary values]]
  371. ifNotNil: [
  372. class methodDictionary values select: [:each |
  373. each category = selectedProtocol]]) sort: [:a :b | a selector > b selector]
  374. !
  375. source
  376. selectedTab = #comment ifFalse: [
  377. ^(selectedProtocol notNil or: [selectedMethod notNil])
  378. ifFalse: [self declarationSource]
  379. ifTrue: [self methodSource]].
  380. ^selectedClass
  381. ifNil: ['']
  382. ifNotNil: [self classCommentSource]
  383. !
  384. methodSource
  385. ^selectedMethod
  386. ifNil: [self dummyMethodSource]
  387. ifNotNil: [selectedMethod source]
  388. !
  389. dummyMethodSource
  390. ^'messageSelectorAndArgumentNames
  391. "comment stating purpose of message"
  392. | temporary variable names |
  393. statements'
  394. !
  395. declarationSource
  396. ^selectedTab = #instance
  397. ifTrue: [self classDeclarationSource]
  398. ifFalse: [self metaclassDeclarationSource]
  399. !
  400. classDeclarationSource
  401. | stream |
  402. stream := '' writeStream.
  403. selectedClass ifNotNil: [
  404. stream
  405. nextPutAll: selectedClass superclass asString;
  406. nextPutAll: ' subclass: #';
  407. nextPutAll: selectedClass name;
  408. nextPutAll: String cr, String tab;
  409. nextPutAll: 'instanceVariableNames: '''.
  410. selectedClass instanceVariableNames
  411. do: [:each | stream nextPutAll: each]
  412. separatedBy: [stream nextPutAll: ' '].
  413. stream
  414. nextPutAll: '''', String cr, String tab;
  415. nextPutAll: 'category: ''';
  416. nextPutAll: selectedClass category;
  417. nextPutAll: ''''].
  418. ^stream contents
  419. !
  420. metaclassDeclarationSource
  421. | stream |
  422. stream := '' writeStream.
  423. selectedClass ifNotNil: [
  424. stream
  425. nextPutAll: selectedClass asString;
  426. nextPutAll: ' class ';
  427. nextPutAll: 'instanceVariableNames: '''.
  428. selectedClass class instanceVariableNames
  429. do: [:each | stream nextPutAll: each]
  430. separatedBy: [stream nextPutAll: ' '].
  431. stream nextPutAll: ''''].
  432. ^stream contents
  433. !
  434. classCommentSource
  435. ^selectedClass comment
  436. ! !
  437. !Browser methodsFor: 'actions'!
  438. enableSaveButton
  439. saveButton removeAt: 'disabled'
  440. !
  441. disableSaveButton
  442. saveButton ifNotNil: [
  443. saveButton at: 'disabled' put: true]
  444. !
  445. hideClassButtons
  446. classButtons asJQuery hide
  447. !
  448. showClassButtons
  449. classButtons asJQuery show
  450. !
  451. hideMethodButtons
  452. methodButtons asJQuery hide
  453. !
  454. showMethodButtons
  455. methodButtons asJQuery show
  456. !
  457. compile
  458. selectedTab = #comment ifTrue: [
  459. selectedClass ifNotNil: [
  460. self compileClassComment]].
  461. (selectedProtocol notNil or: [selectedMethod notNil])
  462. ifFalse: [self compileDefinition]
  463. ifTrue: [self compileMethodDefinition].
  464. self disableSaveButton
  465. !
  466. compileClassComment
  467. selectedClass comment: sourceTextarea asJQuery val
  468. !
  469. compileMethodDefinition
  470. selectedTab = #instance
  471. ifTrue: [self compileMethodDefinitionFor: selectedClass]
  472. ifFalse: [self compileMethodDefinitionFor: selectedClass class]
  473. !
  474. compileMethodDefinitionFor: aClass
  475. | method |
  476. method := Compiler new load: sourceTextarea asJQuery val forClass: selectedClass.
  477. method category: selectedProtocol.
  478. aClass addCompiledMethod: method.
  479. self updateMethodsList.
  480. self selectMethod: method
  481. !
  482. compileDefinition
  483. | newClass |
  484. newClass := Compiler new loadExpression: sourceTextarea asJQuery val.
  485. self
  486. updateCategoriesList;
  487. updateClassesList
  488. !
  489. removeClass
  490. (self confirm: 'Do you really want to remove ', selectedClass name, '?')
  491. ifTrue: [
  492. Smalltalk current basicDelete: selectedClass name.
  493. self selectClass: nil]
  494. !
  495. removeMethod
  496. (self confirm: 'Do you really want to remove #', selectedMethod selector, '?')
  497. ifTrue: [
  498. selectedClass removeCompiledMethod: selectedMethod.
  499. self selectMethod: nil]
  500. !
  501. selectCategory: aCategory
  502. selectedCategory := aCategory.
  503. selectedClass := selectedProtocol := selectedMethod := nil.
  504. self
  505. updateCategoriesList;
  506. updateClassesList;
  507. updateProtocolsList;
  508. updateMethodsList;
  509. updateSourceAndButtons
  510. !
  511. selectClass: aClass
  512. selectedClass := aClass.
  513. selectedProtocol := selectedMethod := nil.
  514. self
  515. updateClassesList;
  516. updateProtocolsList;
  517. updateMethodsList;
  518. updateSourceAndButtons
  519. !
  520. selectProtocol: aString
  521. selectedProtocol := aString.
  522. selectedMethod := nil.
  523. self
  524. updateProtocolsList;
  525. updateMethodsList;
  526. updateSourceAndButtons
  527. !
  528. selectMethod: aMethod
  529. selectedMethod := aMethod.
  530. self
  531. updateProtocolsList;
  532. updateMethodsList;
  533. updateSourceAndButtons
  534. !
  535. selectTab: aString
  536. selectedTab := aString.
  537. self selectProtocol: nil.
  538. self updateTabsList.
  539. ! !
  540. !Browser methodsFor: 'rendering'!
  541. renderBoxOn: html
  542. self
  543. renderTopPanelOn: html;
  544. renderTabsOn: html;
  545. renderBottomPanelOn: html
  546. !
  547. renderTopPanelOn: html
  548. html div
  549. class: 'top';
  550. with: [
  551. categoriesList := html ul class: 'jt_column categories'.
  552. classesList := html ul class: 'jt_column classes'.
  553. protocolsList := html ul class: 'jt_column protocols'.
  554. methodsList := html ul class: 'jt_column methods'.
  555. self
  556. updateCategoriesList;
  557. updateClassesList;
  558. updateProtocolsList;
  559. updateMethodsList.
  560. html div class: 'jt_clear']
  561. !
  562. renderTabsOn: html
  563. tabsList := html ul class: 'jt_tabs'.
  564. self updateTabsList.
  565. !
  566. renderBottomPanelOn: html
  567. html div
  568. class: 'jt_sourceCode';
  569. with: [
  570. sourceTextarea := html textarea
  571. onKeyPress: [self enableSaveButton];
  572. class: 'source';
  573. at: 'spellcheck' put: 'false'.
  574. sourceTextarea asJQuery call: 'tabby']
  575. !
  576. renderButtonsOn: html
  577. saveButton := html button.
  578. saveButton
  579. with: 'Save';
  580. onClick: [self compile].
  581. methodButtons := html span with: [
  582. html button
  583. with: 'Remove method';
  584. onClick: [self removeMethod]].
  585. classButtons := html span with: [
  586. html button
  587. with: 'Remove class';
  588. onClick: [self removeClass]].
  589. self updateSourceAndButtons
  590. ! !
  591. !Browser methodsFor: 'updating'!
  592. updateCategoriesList
  593. categoriesList contents: [:html |
  594. self categories do: [:each || li |
  595. li := html li.
  596. selectedCategory = each ifTrue: [
  597. li class: 'selected'].
  598. li
  599. with: each;
  600. onClick: [self selectCategory: each]]]
  601. !
  602. updateClassesList
  603. TabManager current update.
  604. classesList contents: [:html |
  605. self classes do: [:each || li |
  606. li := html li.
  607. selectedClass = each ifTrue: [
  608. li class: 'selected'].
  609. li
  610. with: each name;
  611. onClick: [self selectClass: each]]]
  612. !
  613. updateProtocolsList
  614. protocolsList contents: [:html |
  615. self protocols do: [:each || li |
  616. li := html li.
  617. selectedProtocol = each ifTrue: [
  618. li class: 'selected'].
  619. li
  620. with: each;
  621. onClick: [self selectProtocol: each]]]
  622. !
  623. updateMethodsList
  624. methodsList contents: [:html |
  625. self methods do: [:each || li |
  626. li := html li.
  627. selectedMethod = each ifTrue: [
  628. li class: 'selected'].
  629. li
  630. with: each selector;
  631. onClick: [self selectMethod: each]]]
  632. !
  633. updateTabsList
  634. tabsList contents: [:html || li |
  635. li := html li.
  636. selectedTab = #instance ifTrue: [li class: 'selected'].
  637. li
  638. with: 'Instance';
  639. onClick: [self selectTab: #instance].
  640. li := html li.
  641. selectedTab = #class ifTrue: [li class: 'selected'].
  642. li
  643. with: 'Class';
  644. onClick: [self selectTab: #class].
  645. li := html li.
  646. selectedTab = #comment ifTrue: [li class: 'selected'].
  647. li
  648. with: 'Comment';
  649. onClick: [self selectTab: #comment]]
  650. !
  651. updateSourceAndButtons
  652. self disableSaveButton.
  653. selectedMethod
  654. ifNil: [
  655. self hideMethodButtons.
  656. selectedClass
  657. ifNil: [self hideClassButtons]
  658. ifNotNil: [self showClassButtons]]
  659. ifNotNil: [
  660. self hideClassButtons.
  661. self showMethodButtons].
  662. sourceTextarea asJQuery val: self source
  663. ! !
  664. !Browser methodsFor: 'testing'!
  665. canBeClosed
  666. ^true
  667. ! !