IDE.st 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. Widget subclass: #TabManager instanceVariableNames: 'selectedTab, tabs, opened' category: 'IDE'! !TabManager methodsFor: 'accessing'! tabs
  2. ^tabs ifNil: [tabs := Array new]
  3. ! ! !TabManager methodsFor: 'actions'! updateBodyMargin
  4. self setBodyMargin: '#jtalk' asJQuery height + 27
  5. ! updatePosition
  6. {'jQuery(''#jtalk'').css(''top'', '''''').css(''bottom'', ''27px'');'}
  7. ! removeBodyMargin
  8. self setBodyMargin: 0
  9. ! setBodyMargin: anInteger
  10. '.jtalkBody' asJQuery cssAt: 'margin-bottom' put: anInteger asString, 'px'
  11. ! onResize: aBlock
  12. {'jQuery(''#jtalk'').resizable({
  13. handles: ''n'',
  14. resize: aBlock,
  15. minHeight: 230
  16. });'}
  17. ! onWindowResize: aBlock
  18. {'jQuery(window).resize(aBlock)'}
  19. ! open
  20. opened ifFalse: [
  21. self root asJQuery show.
  22. 'body' asJQuery addClass: 'jtalkBody'.
  23. '#jtalk' asJQuery show.
  24. self updateBodyMargin.
  25. selectedTab root asJQuery show.
  26. opened := true]
  27. ! close
  28. opened ifTrue: [
  29. self root asJQuery hide.
  30. '#jtalk' asJQuery hide.
  31. self removeBodyMargin.
  32. 'body' asJQuery removeClass: 'jtalkBody'.
  33. opened := false]
  34. ! newBrowserTab
  35. Browser open
  36. ! selectTab: aWidget
  37. self open.
  38. selectedTab := aWidget.
  39. self tabs do: [:each |
  40. each root asJQuery hide].
  41. aWidget root asJQuery show.
  42. self update
  43. ! closeTab: aWidget
  44. self removeTab: aWidget.
  45. self selectTab: self tabs last.
  46. aWidget root asJQuery remove.
  47. self update
  48. ! ! !TabManager methodsFor: 'adding/Removing'! addTab: aWidget
  49. self tabs add: aWidget.
  50. '#jtalk' asJQuery append: aWidget.
  51. aWidget root asJQuery hide
  52. ! removeTab: aWidget
  53. self tabs remove: aWidget.
  54. self update
  55. ! ! !TabManager methodsFor: 'initialization'! initialize
  56. super initialize.
  57. opened := true.
  58. 'body' asJQuery
  59. append: self;
  60. append: [:html | html div id: 'jtalk'];
  61. addClass: 'jtalkBody'.
  62. self
  63. addTab: Transcript current;
  64. addTab: Workspace new.
  65. self selectTab: self tabs last.
  66. self
  67. onResize: [self updateBodyMargin; updatePosition];
  68. onWindowResize: [self updatePosition]
  69. ! ! !TabManager methodsFor: 'rendering'! renderOn: html
  70. html ul
  71. id: 'jtalkTabs';
  72. with: [
  73. html li
  74. class: 'closeAll';
  75. with: 'x';
  76. onClick: [self close].
  77. self tabs do: [:each |
  78. self renderTabFor: each on: html].
  79. html li
  80. class: 'newtab';
  81. with: ' + ';
  82. onClick: [self newBrowserTab]]
  83. ! renderTabFor: aWidget on: html
  84. | li |
  85. li := html li.
  86. selectedTab = aWidget ifTrue: [
  87. li class: 'selected'].
  88. li with: [
  89. html span
  90. with: aWidget label;
  91. onClick: [self selectTab: aWidget].
  92. aWidget canBeClosed ifTrue: [
  93. html span
  94. class: 'close';
  95. with: 'x';
  96. onClick: [self closeTab: aWidget]]]
  97. ! ! TabManager class instanceVariableNames: 'current'! !TabManager class methodsFor: 'instance creation'! current
  98. ^current ifNil: [current := super new]
  99. ! new
  100. self shouldNotImplement
  101. ! ! Widget subclass: #TabWidget instanceVariableNames: '' category: 'IDE'! !TabWidget methodsFor: 'accessing'! label
  102. self subclassResponsibility
  103. ! ! !TabWidget methodsFor: 'actions'! open
  104. TabManager current
  105. addTab: self;
  106. selectTab: self
  107. ! ! !TabWidget methodsFor: 'rendering'! renderOn: html
  108. html root
  109. class: 'jtalkTool';
  110. with: [
  111. html div
  112. class: 'jt_box';
  113. with: [self renderBoxOn: html].
  114. html div
  115. class: 'jt_buttons';
  116. with: [self renderButtonsOn: html]]
  117. ! renderBoxOn: html
  118. ! renderButtonsOn: html
  119. ! ! !TabWidget methodsFor: 'testing'! canBeClosed
  120. ^false
  121. ! ! !TabWidget class methodsFor: 'instance creation'! open
  122. ^self new open
  123. ! ! TabWidget subclass: #Workspace instanceVariableNames: 'textarea' category: 'IDE'! !Workspace methodsFor: 'accessing'! label
  124. ^'[Workspace]'
  125. ! selection
  126. {'return document.selection'}
  127. ! selectionStart
  128. {'return jQuery(''.jt_workspace'')[0].selectionStart'}
  129. ! selectionEnd
  130. {'return jQuery(''.jt_workspace'')[0].selectionEnd'}
  131. ! selectionStart: anInteger
  132. {'jQuery(''.jt_workspace'')[0].selectionStart = anInteger'}
  133. ! selectionEnd: anInteger
  134. {'jQuery(''.jt_workspace'')[0].selectionEnd = anInteger'}
  135. ! currentLine
  136. | lines startLine endLine|
  137. lines := textarea asJQuery val tokenize: String cr.
  138. startLine := endLine := 0.
  139. lines do: [:each |
  140. endLine := startLine + each size.
  141. startLine := endLine + 1.
  142. endLine >= self selectionStart ifTrue: [
  143. self selectionEnd: endLine.
  144. ^each]]
  145. ! ! !Workspace methodsFor: 'actions'! handleKeyDown: anEvent
  146. {'if(anEvent.ctrlKey) {
  147. if(anEvent.keyCode === 80) { //ctrl+p
  148. self._printIt();
  149. anEvent.preventDefault();
  150. return false;
  151. }
  152. if(anEvent.keyCode === 68) { //ctrl+d
  153. self._doIt();
  154. anEvent.preventDefault();
  155. return false;
  156. }
  157. if(anEvent.keyCode === 73) { //ctrl+i
  158. self._inspectIt();
  159. anEvent.preventDefault();
  160. return false;
  161. }
  162. }'}
  163. ! clearWorkspace
  164. textarea asJQuery val: ''
  165. ! doIt
  166. | selection |
  167. textarea asJQuery focus.
  168. self selectionStart = self selectionEnd
  169. ifTrue: [selection := self currentLine]
  170. ifFalse: [
  171. selection := textarea asJQuery val copyFrom: self selectionStart + 1 to: self selectionEnd + 1].
  172. ^self eval: selection ! printIt
  173. self print: self doIt printString
  174. ! print: aString
  175. | start |
  176. start := self selectionEnd.
  177. textarea asJQuery val: (
  178. (textarea asJQuery val copyFrom: 1 to: start),
  179. ' ', aString, ' ',
  180. (textarea asJQuery val copyFrom: start + 1 to: textarea asJQuery val size)).
  181. self selectionStart: start.
  182. self selectionEnd: start + aString size + 2
  183. ! eval: aString
  184. | compiler node |
  185. compiler := Compiler new.
  186. node := compiler parseExpression: aString.
  187. node isParseFailure ifTrue: [
  188. ^self alert: node reason, ', position: ', node position].
  189. ^compiler loadExpression: aString
  190. ! inspectIt
  191. self doIt inspect
  192. ! ! !Workspace methodsFor: 'rendering'! renderBoxOn: html
  193. textarea := html textarea.
  194. textarea asJQuery call: 'tabby'.
  195. textarea onKeyDown: [:e | self handleKeyDown: e].
  196. textarea
  197. class: 'jt_workspace';
  198. at: 'spellcheck' put: 'false'
  199. ! renderButtonsOn: html
  200. html button
  201. with: 'DoIt';
  202. title: 'ctrl+d';
  203. onClick: [self doIt].
  204. html button
  205. with: 'PrintIt';
  206. title: 'ctrl+p';
  207. onClick: [self printIt].
  208. html button
  209. with: 'InspectIt';
  210. title: 'ctrl+i';
  211. onClick: [self inspectIt].
  212. html button
  213. with: 'Clear workspace';
  214. onClick: [self clearWorkspace]
  215. ! ! TabWidget subclass: #Transcript instanceVariableNames: 'textarea' category: 'IDE'! !Transcript methodsFor: 'accessing'! label
  216. ^'[Transcript]'
  217. ! ! !Transcript methodsFor: 'actions'! show: anObject
  218. textarea asJQuery val: textarea asJQuery val, anObject asString.
  219. ! cr
  220. textarea asJQuery val: textarea asJQuery val, String cr.
  221. ! clear
  222. textarea asJQuery val: ''
  223. ! ! !Transcript methodsFor: 'rendering'! renderBoxOn: html
  224. textarea := html textarea.
  225. textarea asJQuery call: 'tabby'.
  226. textarea
  227. class: 'jt_transcript';
  228. at: 'spellcheck' put: 'false'
  229. ! renderButtonsOn: html
  230. html button
  231. with: 'Clear transcript';
  232. onClick: [self clear]
  233. ! ! Transcript class instanceVariableNames: 'current'! !Transcript class methodsFor: 'instance creation'! open
  234. self current open
  235. ! new
  236. self shouldNotImplement
  237. ! current
  238. ^current ifNil: [current := super new]
  239. ! ! !Transcript class methodsFor: 'printing'! show: anObject
  240. self current show: anObject
  241. ! cr
  242. self current show: String cr
  243. ! clear
  244. self current clear
  245. ! ! TabWidget subclass: #Browser instanceVariableNames: 'selectedCategory, selectedClass, selectedProtocol, selectedMethod, commitButton, categoriesList, classesList, protocolsList, methodsList, sourceTextarea, tabsList, selectedTab, saveButton, classButtons, methodButtons, unsavedChanges' category: 'IDE'! !Browser methodsFor: 'accessing'! label
  246. ^selectedClass
  247. ifNil: ['Browser (nil)']
  248. ifNotNil: [selectedClass name]
  249. ! categories
  250. | categories |
  251. categories := Array new.
  252. Smalltalk current classes do: [:each |
  253. (categories includes: each category) ifFalse: [
  254. categories add: each category]].
  255. ^categories sort
  256. ! classes
  257. ^(Smalltalk current classes
  258. select: [:each | each category = selectedCategory])
  259. sort: [:a :b | a name > b name]
  260. ! protocols
  261. | klass |
  262. selectedClass ifNotNil: [
  263. selectedTab = #comment ifTrue: [^#()].
  264. klass := selectedTab = #instance
  265. ifTrue: [selectedClass]
  266. ifFalse: [selectedClass class].
  267. klass methodDictionary isEmpty ifTrue: [
  268. ^Array with: 'not yet classified'].
  269. ^klass protocols].
  270. ^Array new ! methods
  271. | klass |
  272. selectedTab = #comment ifTrue: [^#()].
  273. selectedClass ifNotNil: [
  274. klass := selectedTab = #instance
  275. ifTrue: [selectedClass]
  276. ifFalse: [selectedClass class]].
  277. ^(selectedProtocol
  278. ifNil: [
  279. klass
  280. ifNil: [#()]
  281. ifNotNil: [klass methodDictionary values]]
  282. ifNotNil: [
  283. klass methodDictionary values select: [:each |
  284. each category = selectedProtocol]]) sort: [:a :b | a selector > b selector]
  285. ! source
  286. selectedTab = #comment ifFalse: [
  287. ^(selectedProtocol notNil or: [selectedMethod notNil])
  288. ifFalse: [self declarationSource]
  289. ifTrue: [self methodSource]].
  290. ^selectedClass
  291. ifNil: ['']
  292. ifNotNil: [self classCommentSource]
  293. ! methodSource
  294. ^selectedMethod
  295. ifNil: [self dummyMethodSource]
  296. ifNotNil: [selectedMethod source]
  297. ! dummyMethodSource
  298. ^'messageSelectorAndArgumentNames
  299. "comment stating purpose of message"
  300. | temporary variable names |
  301. statements'
  302. ! declarationSource
  303. ^selectedTab = #instance
  304. ifTrue: [self classDeclarationSource]
  305. ifFalse: [self metaclassDeclarationSource]
  306. ! classDeclarationSource
  307. | stream |
  308. stream := '' writeStream.
  309. selectedClass ifNotNil: [
  310. stream
  311. nextPutAll: selectedClass superclass asString;
  312. nextPutAll: ' subclass: #';
  313. nextPutAll: selectedClass name;
  314. nextPutAll: String cr, String tab;
  315. nextPutAll: 'instanceVariableNames: '''.
  316. selectedClass instanceVariableNames
  317. do: [:each | stream nextPutAll: each]
  318. separatedBy: [stream nextPutAll: ' '].
  319. stream
  320. nextPutAll: '''', String cr, String tab;
  321. nextPutAll: 'category: ''';
  322. nextPutAll: selectedClass category;
  323. nextPutAll: ''''].
  324. ^stream contents
  325. ! metaclassDeclarationSource
  326. | stream |
  327. stream := '' writeStream.
  328. selectedClass ifNotNil: [
  329. stream
  330. nextPutAll: selectedClass asString;
  331. nextPutAll: ' class ';
  332. nextPutAll: 'instanceVariableNames: '''.
  333. selectedClass class instanceVariableNames
  334. do: [:each | stream nextPutAll: each]
  335. separatedBy: [stream nextPutAll: ' '].
  336. stream nextPutAll: ''''].
  337. ^stream contents
  338. ! classCommentSource
  339. ^selectedClass comment
  340. ! ! !Browser methodsFor: 'actions'! enableSaveButton
  341. saveButton removeAt: 'disabled'.
  342. unsavedChanges := true
  343. ! disableSaveButton
  344. saveButton ifNotNil: [
  345. saveButton at: 'disabled' put: true].
  346. unsavedChanges := false
  347. ! hideClassButtons
  348. classButtons asJQuery hide
  349. ! showClassButtons
  350. classButtons asJQuery show
  351. ! hideMethodButtons
  352. methodButtons asJQuery hide
  353. ! showMethodButtons
  354. methodButtons asJQuery show
  355. ! compile
  356. self disableSaveButton.
  357. selectedTab = #comment ifTrue: [
  358. selectedClass ifNotNil: [
  359. self compileClassComment]].
  360. (selectedProtocol notNil or: [selectedMethod notNil])
  361. ifFalse: [self compileDefinition]
  362. ifTrue: [self compileMethodDefinition]
  363. ! compileClassComment
  364. selectedClass comment: sourceTextarea asJQuery val
  365. ! compileMethodDefinition
  366. selectedTab = #instance
  367. ifTrue: [self compileMethodDefinitionFor: selectedClass]
  368. ifFalse: [self compileMethodDefinitionFor: selectedClass class]
  369. ! compileMethodDefinitionFor: aClass
  370. | compiler method source node |
  371. source := sourceTextarea asJQuery val.
  372. selectedProtocol ifNil: [selectedProtocol := selectedMethod category].
  373. compiler := Compiler new.
  374. node := compiler parse: source.
  375. node isParseFailure ifTrue: [
  376. ^self alert: 'PARSE ERROR: ', node reason, ', position: ', node position asString].
  377. compiler currentClass: selectedClass.
  378. method := compiler eval: (compiler compileNode: node).
  379. method category: selectedProtocol.
  380. compiler unknownVariables do: [:each |
  381. (self confirm: 'Declare ''', each, ''' as instance variable?') ifTrue: [
  382. self addInstanceVariableNamed: each toClass: aClass.
  383. ^self compileMethodDefinitionFor: aClass]].
  384. aClass addCompiledMethod: method.
  385. self updateMethodsList.
  386. self selectMethod: method
  387. ! compileDefinition
  388. | newClass |
  389. newClass := Compiler new loadExpression: sourceTextarea asJQuery val.
  390. self
  391. updateCategoriesList;
  392. updateClassesList
  393. ! commitCategory
  394. selectedCategory ifNotNil: [
  395. (Ajax url: self class commitPathJs, '/', selectedCategory, '.js')
  396. at: 'type' put: 'PUT';
  397. at: 'data' put: (Exporter new exportCategory: selectedCategory);
  398. at: 'error' put: [self alert: 'Commit failed!'];
  399. send.
  400. (Ajax url: self class commitPathSt, '/', selectedCategory, '.st')
  401. at: 'type' put: 'PUT';
  402. at: 'data' put: (ChunkExporter new exportCategory: selectedCategory);
  403. at: 'error' put: [self alert: 'Commit failed!'];
  404. send]
  405. ! cancelChanges
  406. ^unsavedChanges
  407. ifTrue: [self confirm: 'Cancel changes?']
  408. ifFalse: [true]
  409. ! removeClass
  410. (self confirm: 'Do you really want to remove ', selectedClass name, '?')
  411. ifTrue: [
  412. Smalltalk current basicDelete: selectedClass name.
  413. self selectClass: nil]
  414. ! removeMethod
  415. self cancelChanges ifTrue: [
  416. (self confirm: 'Do you really want to remove #', selectedMethod selector, '?')
  417. ifTrue: [
  418. selectedTab = #instance
  419. ifTrue: [selectedClass removeCompiledMethod: selectedMethod]
  420. ifFalse: [selectedClass class removeCompiledMethod: selectedMethod].
  421. self selectMethod: nil]]
  422. ! setMethodProtocol: aString
  423. self cancelChanges ifTrue: [
  424. (self protocols includes: aString)
  425. ifFalse: [self addNewProtocol]
  426. ifTrue: [
  427. selectedMethod category: aString.
  428. selectedProtocol := aString.
  429. selectedMethod := selectedMethod.
  430. self
  431. updateProtocolsList;
  432. updateMethodsList;
  433. updateSourceAndButtons]]
  434. ! addNewProtocol
  435. | newProtocol |
  436. newProtocol := self prompt: 'New method protocol'.
  437. newProtocol notEmpty ifTrue: [
  438. selectedMethod category: newProtocol.
  439. self setMethodProtocol: newProtocol]
  440. ! selectCategory: aCategory
  441. self cancelChanges ifTrue: [
  442. selectedCategory := aCategory.
  443. selectedClass := selectedProtocol := selectedMethod := nil.
  444. self
  445. updateCategoriesList;
  446. updateClassesList;
  447. updateProtocolsList;
  448. updateMethodsList;
  449. updateSourceAndButtons]
  450. ! selectClass: aClass
  451. self cancelChanges ifTrue: [
  452. selectedClass := aClass.
  453. selectedProtocol := selectedMethod := nil.
  454. self
  455. updateClassesList;
  456. updateProtocolsList;
  457. updateMethodsList;
  458. updateSourceAndButtons]
  459. ! selectProtocol: aString
  460. self cancelChanges ifTrue: [
  461. selectedProtocol := aString.
  462. selectedMethod := nil.
  463. self
  464. updateProtocolsList;
  465. updateMethodsList;
  466. updateSourceAndButtons]
  467. ! selectMethod: aMethod
  468. self cancelChanges ifTrue: [
  469. selectedMethod := aMethod.
  470. self
  471. updateProtocolsList;
  472. updateMethodsList;
  473. updateSourceAndButtons]
  474. ! selectTab: aString
  475. self cancelChanges ifTrue: [
  476. selectedTab := aString.
  477. self selectProtocol: nil.
  478. self updateTabsList]
  479. ! renameClass
  480. | newName |
  481. newName := self prompt: 'Rename class ', selectedClass name.
  482. newName notEmpty ifTrue: [
  483. selectedClass rename: newName.
  484. self
  485. updateClassesList;
  486. updateSourceAndButtons]
  487. ! addInstanceVariableNamed: aString toClass: aClass
  488. ClassBuilder new
  489. addSubclassOf: aClass superclass named: aClass name instanceVariableNames: (aClass instanceVariableNames copy add: aString; yourself) ! searchReferencesOf: aString
  490. ReferencesBrowser search: aString ! searchClassReferences
  491. ReferencesBrowser search: selectedClass name ! ! !Browser methodsFor: 'initialization'! initialize
  492. super initialize.
  493. selectedTab := #instance.
  494. unsavedChanges := false
  495. ! ! !Browser methodsFor: 'rendering'! renderBoxOn: html
  496. self
  497. renderTopPanelOn: html;
  498. renderTabsOn: html;
  499. renderBottomPanelOn: html
  500. ! renderTopPanelOn: html
  501. html div
  502. class: 'top';
  503. with: [
  504. categoriesList := html ul class: 'jt_column categories'.
  505. commitButton := html button
  506. class: 'jt_commit';
  507. title: 'Commit classes in this category to disk';
  508. onClick: [self commitCategory];
  509. with: 'Commit category'.
  510. classesList := html ul class: 'jt_column classes'.
  511. protocolsList := html ul class: 'jt_column protocols'.
  512. methodsList := html ul class: 'jt_column methods'.
  513. self
  514. updateCategoriesList;
  515. updateClassesList;
  516. updateProtocolsList;
  517. updateMethodsList.
  518. html div class: 'jt_clear']
  519. ! renderTabsOn: html
  520. tabsList := html ul class: 'jt_tabs'.
  521. self updateTabsList.
  522. ! renderBottomPanelOn: html
  523. html div
  524. class: 'jt_sourceCode';
  525. with: [
  526. sourceTextarea := html textarea
  527. onKeyPress: [self enableSaveButton];
  528. class: 'source';
  529. at: 'spellcheck' put: 'false'.
  530. sourceTextarea asJQuery call: 'tabby']
  531. ! renderButtonsOn: html
  532. saveButton := html button.
  533. saveButton
  534. with: 'Save';
  535. onClick: [self compile].
  536. methodButtons := html span.
  537. classButtons := html span.
  538. self updateSourceAndButtons
  539. ! ! !Browser methodsFor: 'testing'! canBeClosed
  540. ^true
  541. ! ! !Browser methodsFor: 'updating'! updateCategoriesList
  542. categoriesList contents: [:html |
  543. self categories do: [:each || li label |
  544. each isEmpty
  545. ifTrue: [label := 'Unclassified']
  546. ifFalse: [label := each].
  547. li := html li.
  548. selectedCategory = each ifTrue: [
  549. li class: 'selected'].
  550. li
  551. with: label;
  552. onClick: [self selectCategory: each]]]
  553. ! updateClassesList
  554. TabManager current update.
  555. classesList contents: [:html |
  556. self classes do: [:each || li |
  557. li := html li.
  558. selectedClass = each ifTrue: [
  559. li class: 'selected'].
  560. li
  561. with: each name;
  562. onClick: [self selectClass: each]]]
  563. ! updateProtocolsList
  564. protocolsList contents: [:html |
  565. self protocols do: [:each || li |
  566. li := html li.
  567. selectedProtocol = each ifTrue: [
  568. li class: 'selected'].
  569. li
  570. with: each;
  571. onClick: [self selectProtocol: each]]]
  572. ! updateMethodsList
  573. methodsList contents: [:html |
  574. self methods do: [:each || li |
  575. li := html li.
  576. selectedMethod = each ifTrue: [
  577. li class: 'selected'].
  578. li
  579. with: each selector;
  580. onClick: [self selectMethod: each]]]
  581. ! updateTabsList
  582. tabsList contents: [:html || li |
  583. li := html li.
  584. selectedTab = #instance ifTrue: [li class: 'selected'].
  585. li
  586. with: 'Instance';
  587. onClick: [self selectTab: #instance].
  588. li := html li.
  589. selectedTab = #class ifTrue: [li class: 'selected'].
  590. li
  591. with: 'Class';
  592. onClick: [self selectTab: #class].
  593. li := html li.
  594. selectedTab = #comment ifTrue: [li class: 'selected'].
  595. li
  596. with: 'Comment';
  597. onClick: [self selectTab: #comment]]
  598. ! updateSourceAndButtons
  599. self disableSaveButton.
  600. classButtons contents: [:html |
  601. html button
  602. with: 'Rename class';
  603. onClick: [self renameClass].
  604. html button
  605. with: 'Remove class';
  606. onClick: [self removeClass].
  607. html button
  608. with: 'References';
  609. onClick: [self searchClassReferences]].
  610. methodButtons contents: [:html |
  611. html button
  612. with: 'Remove method';
  613. onClick: [self removeMethod].
  614. html select
  615. onChange: [:e :select | self setMethodProtocol: select val];
  616. with: [
  617. html option
  618. with: 'Method protocol';
  619. at: 'disabled' put: 'disabled'.
  620. html option
  621. class: 'important';
  622. with: 'New...'.
  623. self protocols do: [:each |
  624. html option with: each]].
  625. selectedMethod isNil ifFalse: [
  626. html select
  627. onChange: [:e :select | self searchReferencesOf: select val];
  628. with: [
  629. html option
  630. with: 'References';
  631. at: 'disabled' put: 'disabled'.
  632. html option
  633. class: 'important';
  634. with: selectedMethod selector.
  635. selectedMethod messageSends sorted do: [:each |
  636. html option with: each]]]].
  637. selectedMethod isNil
  638. ifTrue: [
  639. self hideMethodButtons.
  640. (selectedClass isNil or: [selectedProtocol notNil])
  641. ifTrue: [self hideClassButtons]
  642. ifFalse: [self showClassButtons]]
  643. ifFalse: [
  644. self hideClassButtons.
  645. self showMethodButtons].
  646. sourceTextarea asJQuery val: self source
  647. ! ! !Browser class methodsFor: 'accessing'! commitPathJs
  648. ^'js' ! commitPathSt
  649. ^'st' ! ! !Browser class methodsFor: 'convenience'! openOn: aClass
  650. ^self new
  651. open;
  652. selectCategory: aClass category;
  653. selectClass: aClass
  654. ! open
  655. self new open
  656. ! ! TabWidget subclass: #Inspector instanceVariableNames: 'label, variables, object, selectedVariable, variablesList, valueTextarea, workspaceTextarea, diveButton' category: 'IDE'! !Inspector methodsFor: 'accessing'! label
  657. ^label ifNil: ['Inspector (nil)'] ! variables
  658. ^variables ! setVariables: aCollection
  659. variables := aCollection ! setLabel: aString
  660. label := aString ! selectedVariable
  661. ^selectedVariable ! selectedVariable: aString
  662. selectedVariable := aString ! ! !Inspector methodsFor: 'actions'! inspect: anObject
  663. object := anObject.
  664. variables := #().
  665. object inspectOn: self ! dive
  666. (self variables at: self selectedVariable) inspect ! refresh
  667. self
  668. inspect: object;
  669. updateVariablesList;
  670. updateValueTextarea ! ! !Inspector methodsFor: 'rendering'! renderBoxOn: html
  671. self
  672. renderTopPanelOn: html;
  673. renderBottomPanelOn: html ! renderTopPanelOn: html
  674. html div
  675. class: 'top';
  676. with: [
  677. variablesList := html ul class: 'jt_column variables'.
  678. valueTextarea := html textarea class: 'jt_column value'; at: 'readonly' put: 'readonly'.
  679. self
  680. updateVariablesList;
  681. updateValueTextarea.
  682. html div class: 'jt_clear']
  683. ! renderBottomPanelOn: html
  684. html div
  685. class: 'jt_sourceCode';
  686. with: [
  687. workspaceTextarea := html textarea
  688. class: 'source';
  689. at: 'spellcheck' put: 'false'.
  690. workspaceTextarea asJQuery call: 'tabby']
  691. ! renderButtonsOn: html
  692. html button
  693. with: 'Refresh';
  694. onClick: [self refresh].
  695. diveButton := html button
  696. with: 'Dive';
  697. onClick: [self dive].
  698. self updateButtons
  699. ! ! !Inspector methodsFor: 'testing'! canBeClosed
  700. ^true ! ! !Inspector methodsFor: 'updating'! updateVariablesList
  701. variablesList contents: [:html |
  702. self variables keys do: [:each || li |
  703. li := html li.
  704. li
  705. with: each;
  706. onClick: [self selectVariable: each].
  707. self selectedVariable = each ifTrue: [
  708. li class: 'selected']]] ! selectVariable: aString
  709. self selectedVariable: aString.
  710. self
  711. updateVariablesList;
  712. updateValueTextarea;
  713. updateButtons ! updateValueTextarea
  714. valueTextarea asJQuery val: (self selectedVariable isNil
  715. ifTrue: ['']
  716. ifFalse: [(self variables at: self selectedVariable) printString]) ! updateButtons
  717. (self selectedVariable notNil and: [(self variables at: self selectedVariable) notNil])
  718. ifFalse: [diveButton at: 'disabled' put: true]
  719. ifTrue: [diveButton removeAt: 'disabled']
  720. ! ! !Inspector class methodsFor: 'instance creation'! on: anObject
  721. ^self new
  722. inspect: anObject;
  723. yourself ! ! TabWidget subclass: #ReferencesBrowser instanceVariableNames: 'implementors, senders, implementorsList, input, timer, selector, sendersList, referencedClasses, referencedClassesList' category: 'IDE'! !ReferencesBrowser methodsFor: 'accessing'! implementors
  724. ^implementors ifNil: [implementors := Array new] ! label
  725. ^'[ReferencesBrowser]' ! selector
  726. ^selector ! senders
  727. ^senders ifNil: [senders := Array new] ! classesAndMetaclasses
  728. ^Smalltalk current classes, (Smalltalk current classes collect: [:each | each class]) ! referencedClasses
  729. ^referencedClasses ifNil: [referencedClasses := Array new] ! ! !ReferencesBrowser methodsFor: 'actions'! openBrowserOn: aMethod
  730. | browser |
  731. browser := Browser openOn: (aMethod class isMetaclass
  732. ifTrue: [aMethod methodClass instanceClass] ifFalse: [aMethod methodClass]).
  733. aMethod methodClass isMetaclass ifTrue: [browser selectTab: #class].
  734. browser
  735. selectProtocol: aMethod category;
  736. selectMethod: aMethod ! searchReferencesFor: aString
  737. selector := aString.
  738. implementors := Array new.
  739. senders := Array new.
  740. referencedClasses := Array new.
  741. (selector match: '^[A-Z]')
  742. ifFalse: [self searchSelectorReferencesFor: selector]
  743. ifTrue: [self searchReferencedClassesFor: selector] ! search: aString
  744. self
  745. searchReferencesFor: aString;
  746. updateImplementorsList;
  747. updateSendersList;
  748. updateReferencedClassesList ! searchReferencedClassesFor: aString
  749. self classesAndMetaclasses do: [:each |
  750. each methodDictionary values do: [:value |
  751. (((value referencedClasses select: [:each | each notNil])collect: [:each | each name]) includes: selector) ifTrue: [
  752. self referencedClasses add: value]]] ! searchSelectorReferencesFor: aString
  753. self classesAndMetaclasses do: [:each |
  754. each methodDictionary keysAndValuesDo: [:key :value |
  755. key = selector ifTrue: [self implementors add: value]].
  756. each methodDictionary keysAndValuesDo: [:key :value |
  757. (value messageSends includes: selector) ifTrue: [
  758. self senders add: value]]] ! ! !ReferencesBrowser methodsFor: 'initialization'! initialize
  759. super initialize.
  760. selector := '' ! ! !ReferencesBrowser methodsFor: 'private'! setInputEvents
  761. input
  762. onKeyUp: [timer := [self search: input asJQuery val] valueWithTimeout: 100];
  763. onKeyDown: [timer ifNotNil: [timer clearTimeout]] ! ! !ReferencesBrowser methodsFor: 'rendering'! renderBoxOn: html
  764. self
  765. renderInputOn: html;
  766. renderImplementorsOn: html;
  767. renderSendersOn: html;
  768. renderReferencedClassesOn: html ! renderInputOn: html
  769. input := html input
  770. class: 'implementors';
  771. yourself.
  772. input asJQuery val: selector.
  773. self setInputEvents ! renderImplementorsOn: html
  774. implementorsList := html ul class: 'jt_column implementors'.
  775. self updateImplementorsList ! renderSendersOn: html
  776. sendersList := html ul class: 'jt_column senders'.
  777. self updateSendersList ! renderReferencedClassesOn: html
  778. referencedClassesList := html ul class: 'jt_column referenced_classes'.
  779. self updateReferencedClassesList ! ! !ReferencesBrowser methodsFor: 'testing'! canBeClosed
  780. ^true ! ! !ReferencesBrowser methodsFor: 'updating'! updateImplementorsList
  781. implementorsList contents: [:html |
  782. html li
  783. class: 'column_label';
  784. with: 'Implementors';
  785. style: 'font-weight: bold'.
  786. self implementors do: [:each || li |
  787. li := html li.
  788. li
  789. with: (each methodClass asString, ' >> ', self selector);
  790. onClick: [self openBrowserOn: each]]] ! updateSendersList
  791. sendersList contents: [:html |
  792. html li
  793. class: 'column_label';
  794. with: 'Senders';
  795. style: 'font-weight: bold'.
  796. self senders do: [:each |
  797. html li
  798. with: (each methodClass asString, ' >> ', each selector);
  799. onClick: [self openBrowserOn: each]]] ! updateReferencedClassesList
  800. referencedClassesList contents: [:html |
  801. html li
  802. class: 'column_label';
  803. with: 'Class references';
  804. style: 'font-weight: bold'.
  805. self referencedClasses do: [:each |
  806. html li
  807. with: (each methodClass asString, ' >> ', each selector);
  808. onClick: [self openBrowserOn: each]]] ! ! !ReferencesBrowser class methodsFor: 'instance creation'! search: aString
  809. ^self new
  810. searchReferencesFor: aString;
  811. open ! ! inspect
  812. Inspector new
  813. inspect: self;
  814. open ! inspectOn: anInspector
  815. | variables |
  816. variables := Dictionary new.
  817. variables at: '#self' put: self.
  818. self class instanceVariableNames do: [:each |
  819. variables at: each put: (self instVarAt: each)].
  820. anInspector
  821. setLabel: self printString;
  822. setVariables: variables
  823. ! inspectOn: anInspector
  824. | variables |
  825. variables := Dictionary new.
  826. variables at: '#self' put: self.
  827. variables at: '#year' put: self year.
  828. variables at: '#month' put: self month.
  829. variables at: '#day' put: self day.
  830. variables at: '#hours' put: self hours.
  831. variables at: '#minutes' put: self minutes.
  832. variables at: '#seconds' put: self seconds.
  833. variables at: '#milliseconds' put: self milliseconds.
  834. anInspector
  835. setLabel: self printString;
  836. setVariables: variables
  837. ! inspectOn: anInspector
  838. | variables |
  839. variables := Dictionary new.
  840. variables at: '#self' put: self.
  841. self withIndexDo: [:each :i |
  842. variables at: i put: each].
  843. anInspector
  844. setLabel: self printString;
  845. setVariables: variables ! inspectOn: anInspector
  846. | label |
  847. super inspectOn: anInspector.
  848. self printString size > 30
  849. ifTrue: [label := (self printString copyFrom: 1 to: 30), '...''']
  850. ifFalse: [label := self printString].
  851. anInspector setLabel: label ! inspectOn: anInspector
  852. | variables |
  853. variables := Dictionary new.
  854. variables at: '#self' put: self.
  855. variables at: '#keys' put: self keys.
  856. self keysAndValuesDo: [:key :value |
  857. variables at: key put: value].
  858. anInspector
  859. setLabel: self printString;
  860. setVariables: variables !