Helios-Workspace.st 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. Smalltalk current createPackage: 'Helios-Workspace'!
  2. Object subclass: #HLCodeModel
  3. instanceVariableNames: 'announcer environment receiver'
  4. package: 'Helios-Workspace'!
  5. !HLCodeModel methodsFor: 'accessing'!
  6. announcer
  7. ^ announcer ifNil: [ announcer := Announcer new ]
  8. !
  9. environment
  10. ^ environment ifNil: [ HLManager current environment ]
  11. !
  12. environment: anEnvironment
  13. environment := anEnvironment
  14. !
  15. receiver
  16. ^ receiver ifNil: [ receiver := self defaultReceiver ]
  17. !
  18. receiver: anObject
  19. receiver := anObject
  20. ! !
  21. !HLCodeModel methodsFor: 'actions'!
  22. doIt: aString
  23. ^ self environment eval: aString on: self receiver
  24. ! !
  25. !HLCodeModel methodsFor: 'defaults'!
  26. defaultReceiver
  27. ^ DoIt new
  28. ! !
  29. !HLCodeModel class methodsFor: 'actions'!
  30. on: anEnvironment
  31. ^ self new
  32. environment: anEnvironment;
  33. yourself
  34. ! !
  35. HLWidget subclass: #HLCodeWidget
  36. instanceVariableNames: 'model wrapper code editor state'
  37. package: 'Helios-Workspace'!
  38. !HLCodeWidget methodsFor: 'accessing'!
  39. announcer
  40. ^ self model announcer
  41. !
  42. contents
  43. ^ editor getValue
  44. !
  45. contents: aString
  46. editor setValue: aString.
  47. state ifNotNil: [ self updateState ]
  48. !
  49. currentLine
  50. ^editor getLine: (editor getCursor line)
  51. !
  52. currentLineOrSelection
  53. ^editor somethingSelected
  54. ifFalse: [ self currentLine ]
  55. ifTrue: [ self selection ]
  56. !
  57. model
  58. ^ model ifNil: [ model := HLCodeModel new ]
  59. !
  60. model: aModel
  61. model := aModel
  62. !
  63. receiver
  64. ^ self model receiver
  65. !
  66. receiver: anObject
  67. self model receiver: anObject
  68. !
  69. selection
  70. ^editor getSelection
  71. !
  72. selectionEnd
  73. ^code element selectionEnd
  74. !
  75. selectionEnd: anInteger
  76. code element selectionEnd: anInteger
  77. !
  78. selectionStart
  79. ^code element selectionStart
  80. !
  81. selectionStart: anInteger
  82. code element selectionStart: anInteger
  83. ! !
  84. !HLCodeWidget methodsFor: 'actions'!
  85. clear
  86. self contents: ''
  87. !
  88. configureEditor
  89. self editor at: 'amberCodeWidget' put: self.
  90. self editor on: 'change' do: [ self onChange ]
  91. !
  92. doIt
  93. | result |
  94. self model announcer announce: (HLDoItRequested on: model).
  95. result := model doIt: self currentLineOrSelection.
  96. self model announcer announce: (HLDoItExecuted on: model).
  97. ^ result
  98. !
  99. editor
  100. ^ editor
  101. !
  102. focus
  103. editor focus
  104. !
  105. inspectIt
  106. | newInspector |
  107. self model announcer announce: (HLInspectItRequested on: model).
  108. self doIt inspect
  109. !
  110. print: aString
  111. | start stop currentLine |
  112. currentLine := (editor getCursor: false) line.
  113. start := HashedCollection new.
  114. start at: 'line' put: currentLine.
  115. start at: 'ch' put: (editor getCursor: false) ch.
  116. (editor getSelection) ifEmpty: [
  117. "select current line if selection is empty"
  118. start at: 'ch' put: (editor getLine: currentLine) size.
  119. editor setSelection: #{'line' -> currentLine. 'ch' -> 0} end: start.
  120. ].
  121. stop := HashedCollection new.
  122. stop at: 'line' put: currentLine.
  123. stop at: 'ch' put: ((start at: 'ch') + aString size + 2).
  124. editor replaceSelection: (editor getSelection, ' ', aString, ' ').
  125. editor setCursor: (editor getCursor: true).
  126. editor setSelection: stop end: start
  127. !
  128. printIt
  129. | result |
  130. result:= self doIt.
  131. self model announcer announce: (HLPrintItRequested on: model).
  132. self print: result printString.
  133. self focus.
  134. !
  135. saveIt
  136. "I do not do anything"
  137. !
  138. setEditorOn: aTextarea
  139. <self['@editor'] = CodeMirror.fromTextArea(aTextarea, {
  140. theme: 'amber',
  141. lineNumbers: true,
  142. enterMode: 'flat',
  143. indentWithTabs: true,
  144. indentUnit: 4,
  145. matchBrackets: true,
  146. electricChars: true,
  147. keyMap: 'Amber',
  148. extraKeys: {"Shift-Space": "autocomplete"}
  149. })>
  150. ! !
  151. !HLCodeWidget methodsFor: 'hints'!
  152. messageHintFor: anEditor token: aToken
  153. ^ ((Smalltalk current at: 'allSelectors') value asSet asArray
  154. select: [ :each | each includesSubString: aToken string ])
  155. reject: [ :each | each = aToken string ]
  156. !
  157. variableHintFor: anEditor token: aToken
  158. | variables classNames pseudoVariables |
  159. variables := ((window jQuery: anEditor display wrapper) find: 'span.cm-variable') get
  160. collect: [ :each | (window jQuery: each) html ].
  161. classNames := Smalltalk current classes collect: [ :each | each name ].
  162. pseudoVariables := Smalltalk current pseudoVariableNames.
  163. ^ ((variables, classNames, pseudoVariables) asSet asArray
  164. select: [ :each | each includesSubString: aToken string ])
  165. reject: [ :each | each = aToken string ]
  166. ! !
  167. !HLCodeWidget methodsFor: 'reactions'!
  168. onChange
  169. self updateState
  170. !
  171. onDoIt
  172. self doIt
  173. !
  174. onInspectIt
  175. self inspectIt
  176. !
  177. onPrintIt
  178. self printIt
  179. !
  180. onSaveIt
  181. "I do not do anything"
  182. ! !
  183. !HLCodeWidget methodsFor: 'rendering'!
  184. renderButtonsOn: html
  185. html button
  186. class: 'button';
  187. with: 'DoIt';
  188. onClick: [ self doIt ].
  189. html button
  190. class: 'button';
  191. with: 'PintIt';
  192. onClick: [ self printIt ].
  193. html button
  194. class: 'button';
  195. with: 'InspectIt';
  196. onClick: [ self inspectIt ]
  197. !
  198. renderContentOn: html
  199. code := html textarea.
  200. state := html div class: 'state'.
  201. html div
  202. class: 'buttons_bar';
  203. with: [ self renderButtonsOn: html ].
  204. self
  205. setEditorOn: code element;
  206. configureEditor;
  207. updateState
  208. ! !
  209. !HLCodeWidget methodsFor: 'testing'!
  210. canHaveFocus
  211. ^ true
  212. !
  213. hasFocus
  214. ^ code asJQuery is: ':active'
  215. !
  216. hasModification
  217. ^ false
  218. ! !
  219. !HLCodeWidget methodsFor: 'updating'!
  220. updateState
  221. self hasModification
  222. ifTrue: [ state asJQuery addClass: 'modified' ]
  223. ifFalse: [ state asJQuery removeClass: 'modified' ]
  224. ! !
  225. !HLCodeWidget class methodsFor: 'accessing'!
  226. keyMap
  227. ^ HLManager current keyBinder systemIsMac
  228. ifTrue: [ self macKeyMap ]
  229. ifFalse: [ self pcKeyMap ]
  230. !
  231. macKeyMap
  232. ^ #{
  233. 'Alt-Backspace' -> 'delWordBefore'.
  234. 'Alt-Delete' -> 'delWordAfter'.
  235. 'Alt-Left' -> 'goWordBoundaryLeft'.
  236. 'Alt-Right' -> 'goWordBoundaryRight'.
  237. 'Cmd-A' -> 'selectAll'.
  238. 'Cmd-Alt-F' -> 'replace'.
  239. 'Cmd-D' -> 'doIt'.
  240. 'Cmd-Down' -> 'goDocEnd'.
  241. 'Cmd-End' -> 'goDocEnd'.
  242. 'Cmd-F' -> 'find'.
  243. 'Cmd-G' -> 'findNext'.
  244. 'Cmd-I' -> 'inspectIt'.
  245. 'Cmd-Left' -> 'goLineStart'.
  246. 'Cmd-P' -> 'printIt'.
  247. 'Cmd-Right' -> 'goLineEnd'.
  248. 'Cmd-S' -> 'saveIt'.
  249. 'Cmd-Up' -> 'goDocStart'.
  250. 'Cmd-Y' -> 'redo'.
  251. 'Cmd-Z' -> 'undo'.
  252. 'Cmd-[' -> 'indentLess'.
  253. 'Cmd-]' -> 'indentMore'.
  254. 'Ctrl-Alt-Backspace' -> 'delWordAfter'.
  255. 'Shift-Cmd-Alt-F' -> 'replaceAll'.
  256. 'Shift-Cmd-G' -> 'findPrev'.
  257. 'Shift-Cmd-Z' -> 'redo'.
  258. 'fallthrough' -> { 'basic'. 'emacsy' }
  259. }
  260. !
  261. pcKeyMap
  262. ^ {
  263. 'Alt-Left' -> 'goLineStart'.
  264. 'Alt-Right' -> 'goLineEnd'.
  265. 'Alt-Up' -> 'goDocStart'.
  266. 'Ctrl-A' -> 'selectAll'.
  267. 'Ctrl-Backspace' -> 'delWordBefore'.
  268. 'Ctrl-D' -> 'doIt'.
  269. 'Ctrl-Delete' -> 'delWordAfter'.
  270. 'Ctrl-Down' -> 'goDocEnd'.
  271. 'Ctrl-End' -> 'goDocEnd'.
  272. 'Ctrl-F' -> 'find'.
  273. 'Ctrl-G' -> 'findNext'.
  274. 'Ctrl-I' -> 'inspectIt'.
  275. 'Ctrl-Home' -> 'goDocStart'.
  276. 'Ctrl-Left' -> 'goWordBoundaryLeft'.
  277. 'Ctrl-P' -> 'printIt'.
  278. 'Ctrl-Right' -> 'goWordBoundaryRight'.
  279. 'Ctrl-S' -> 'saveIt'.
  280. 'Ctrl-Y' -> 'redo'.
  281. 'Ctrl-Z' -> 'undo'.
  282. 'Ctrl-[' -> 'indentLess'.
  283. 'Ctrl-]' -> 'indentMore'.
  284. 'Shift-Ctrl-F' -> 'replace'.
  285. 'Shift-Ctrl-G' -> 'findPrev'.
  286. 'Shift-Ctrl-R' -> 'replaceAll'.
  287. 'Shift-Ctrl-Z' -> 'redo'.
  288. 'fallthrough' -> #('basic')
  289. }
  290. ! !
  291. !HLCodeWidget class methodsFor: 'hints'!
  292. hintFor: anEditor options: options
  293. | cursor token completions |
  294. cursor := anEditor getCursor.
  295. token := anEditor getTokenAt: cursor.
  296. token at: 'state' put: ((CodeMirror basicAt: 'innerMode')
  297. value: anEditor getMode value: (token at: 'state')) state.
  298. completions := token type = 'variable'
  299. ifTrue: [ HLCodeWidget variableHintFor: anEditor token: token ]
  300. ifFalse: [ HLCodeWidget messageHintFor: anEditor token: token ].
  301. ^ #{
  302. 'list' -> completions.
  303. 'from' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token end).
  304. 'to' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token start)
  305. }
  306. !
  307. messageHintFor: anEditor token: aToken
  308. ^ (anEditor at: 'amberCodeWidget')
  309. messageHintFor: anEditor token: aToken
  310. !
  311. variableHintFor: anEditor token: aToken
  312. ^ (anEditor at: 'amberCodeWidget')
  313. variableHintFor: anEditor token: aToken
  314. ! !
  315. !HLCodeWidget class methodsFor: 'initialization'!
  316. initialize
  317. super initialize.
  318. self
  319. setupCodeMirror;
  320. setupCommands;
  321. setupKeyMaps.
  322. !
  323. setupCodeMirror
  324. <
  325. CodeMirror.keyMap.default.fallthrough = ["basic"];
  326. CodeMirror.commands.autocomplete = function(cm) {
  327. CodeMirror.showHint(cm, self._hintFor_options_);
  328. }
  329. >
  330. !
  331. setupCommands
  332. (CodeMirror basicAt: 'commands')
  333. at: 'doIt' put: [ :cm | cm amberCodeWidget doIt ];
  334. at: 'inspectIt' put: [ :cm | cm amberCodeWidget inspectIt ];
  335. at: 'printIt' put: [ :cm | cm amberCodeWidget printIt ];
  336. at: 'saveIt' put: [ :cm | cm amberCodeWidget saveIt ]
  337. !
  338. setupKeyMaps
  339. <CodeMirror.keyMap['Amber'] = self._keyMap()>
  340. ! !
  341. HLCodeWidget subclass: #HLNavigationCodeWidget
  342. instanceVariableNames: 'methodContents'
  343. package: 'Helios-Workspace'!
  344. !HLNavigationCodeWidget methodsFor: 'accessing'!
  345. configureEditor
  346. super configureEditor.
  347. self contents: self methodContents
  348. !
  349. contents: aString
  350. self methodContents: aString.
  351. super contents: aString
  352. !
  353. methodContents
  354. ^ methodContents ifNil: [ '' ]
  355. !
  356. methodContents: aString
  357. ^ methodContents := aString
  358. !
  359. previous
  360. "for browser lists widget"
  361. !
  362. previous: aWidget
  363. "for browser lists widget"
  364. ! !
  365. !HLNavigationCodeWidget methodsFor: 'testing'!
  366. hasModification
  367. ^ (self methodContents = self contents) not
  368. ! !
  369. !HLNavigationCodeWidget class methodsFor: 'instance creation'!
  370. on: aBrowserModel
  371. ^ self new
  372. browserModel: aBrowserModel;
  373. yourself
  374. ! !
  375. !HLNavigationCodeWidget class methodsFor: 'testing'!
  376. canBeOpenAsTab
  377. ^ false
  378. ! !
  379. HLNavigationCodeWidget subclass: #HLBrowserCodeWidget
  380. instanceVariableNames: 'browserModel'
  381. package: 'Helios-Workspace'!
  382. !HLBrowserCodeWidget methodsFor: 'accessing'!
  383. browserModel
  384. ^ browserModel
  385. !
  386. browserModel: aBrowserModel
  387. browserModel := aBrowserModel.
  388. self
  389. observeSystem;
  390. observeBrowserModel
  391. ! !
  392. !HLBrowserCodeWidget methodsFor: 'actions'!
  393. observeBrowserModel
  394. self browserModel announcer
  395. on: HLSaveSourceCode
  396. send: #onSaveIt
  397. to: self;
  398. on: HLShowInstanceToggled
  399. send: #onShowInstanceToggled
  400. to: self;
  401. on: HLSourceCodeSaved
  402. send: #onSourceCodeSaved
  403. to: self;
  404. on: HLAboutToChange
  405. send: #onBrowserAboutToChange:
  406. to: self;
  407. on: HLParseErrorRaised
  408. send: #onParseError:
  409. to: self;
  410. on: HLCompileErrorRaised
  411. send: #onCompileError:
  412. to: self;
  413. on: HLUnknownVariableErrorRaised
  414. send: #onUnknownVariableError:
  415. to: self;
  416. on: HLInstVarAdded
  417. send: #onInstVarAdded
  418. to: self;
  419. on: HLMethodSelected
  420. send: #onMethodSelected:
  421. to: self;
  422. on: HLClassSelected
  423. send: #onClassSelected:
  424. to: self;
  425. on: HLProtocolSelected
  426. send: #onProtocolSelected:
  427. to: self;
  428. on: HLSourceCodeFocusRequested
  429. send: #onSourceCodeFocusRequested
  430. to: self
  431. !
  432. observeSystem
  433. self browserModel systemAnnouncer
  434. on: MethodModified
  435. send: #onMethodModified:
  436. to: self
  437. !
  438. refresh
  439. self hasModification ifTrue: [ ^ self ].
  440. self hasFocus ifTrue: [ ^ self ].
  441. self contents: self browserModel selectedMethod source
  442. !
  443. renderButtonsOn: html
  444. html button
  445. class: 'button';
  446. with: 'SaveIt';
  447. onClick: [ self saveIt ].
  448. super renderButtonsOn: html
  449. !
  450. saveIt
  451. self browserModel saveSourceCode
  452. !
  453. unregister
  454. super unregsiter.
  455. self browserModel announcer unsubscribe: self.
  456. self browserModel systemAnnouncer unsubscribe: self
  457. ! !
  458. !HLBrowserCodeWidget methodsFor: 'reactions'!
  459. onBrowserAboutToChange: anAnnouncement
  460. | block |
  461. block := anAnnouncement actionBlock.
  462. self hasModification
  463. ifTrue: [
  464. self
  465. confirm: 'Do you want to cancel changes?'
  466. ifTrue: [
  467. "Don't ask twice"
  468. self methodContents: self contents.
  469. block value ].
  470. HLChangeForbidden signal ]
  471. !
  472. onClassSelected: anAnnouncement
  473. | class |
  474. class:= anAnnouncement item.
  475. class ifNil: [ ^ self contents: '' ].
  476. self contents: class definition
  477. !
  478. onCompileError: anAnnouncement
  479. self alert: anAnnouncement error messageText
  480. !
  481. onInstVarAdded
  482. self browserModel save: self contents
  483. !
  484. onMethodModified: anAnnouncement
  485. | method |
  486. method := anAnnouncement method.
  487. self browserModel selectedClass = method methodClass ifFalse: [ ^ self ].
  488. self browserModel selectedMethod ifNil: [ ^ self ].
  489. self browserModel selectedMethod selector = method selector ifFalse: [ ^ self ].
  490. self refresh
  491. !
  492. onMethodSelected: anAnnouncement
  493. | method |
  494. method := anAnnouncement item.
  495. method ifNil: [ ^ self contents: '' ].
  496. self contents: method source
  497. !
  498. onParseError: anAnnouncement
  499. | lineIndex newContents |
  500. lineIndex := 1.
  501. self contents: (String streamContents: [ :stream |
  502. self contents linesDo: [ :each |
  503. lineIndex = anAnnouncement line
  504. ifTrue: [
  505. stream
  506. nextPutAll: (each copyFrom: 1 to: anAnnouncement column);
  507. nextPutAll: '<- ';
  508. nextPutAll: anAnnouncement message;
  509. nextPutAll: ' ';
  510. nextPutAll: (each copyFrom: anAnnouncement column + 1 to: each size) ]
  511. ifFalse: [ stream nextPutAll: each ].
  512. stream nextPutAll: String cr.
  513. lineIndex := lineIndex + 1 ] ])
  514. !
  515. onProtocolSelected: anAnnouncement
  516. self browserModel selectedClass ifNil: [ ^ self contents: '' ].
  517. self contents: self browserModel selectedClass definition
  518. !
  519. onSaveIt
  520. self browserModel save: self contents
  521. !
  522. onShowInstanceToggled
  523. self browserModel selectedClass ifNil: [ ^ self contents: '' ].
  524. self contents: self browserModel selectedClass definition
  525. !
  526. onSourceCodeFocusRequested
  527. self focus
  528. !
  529. onSourceCodeSaved
  530. self methodContents: self contents.
  531. self updateState
  532. !
  533. onUnknownVariableError: anAnnouncement
  534. | error |
  535. error := anAnnouncement error.
  536. self
  537. confirm: (String streamContents: [ :stream |
  538. stream
  539. nextPutAll: error messageText;
  540. nextPutAll: String cr;
  541. nextPutAll: 'Would you like to define an instance variable?' ])
  542. ifTrue: [
  543. self browserModel addInstVarNamed: error variableName ]
  544. ! !
  545. !HLBrowserCodeWidget class methodsFor: 'instance creation'!
  546. on: aBrowserModel
  547. ^ self new
  548. browserModel: aBrowserModel;
  549. yourself
  550. ! !
  551. !HLBrowserCodeWidget class methodsFor: 'testing'!
  552. canBeOpenAsTab
  553. ^ false
  554. ! !
  555. HLWidget subclass: #HLWorkspace
  556. instanceVariableNames: 'codeWidget'
  557. package: 'Helios-Workspace'!
  558. !HLWorkspace methodsFor: 'accessing'!
  559. codeWidget
  560. ^ codeWidget ifNil: [ codeWidget := HLCodeWidget new ]
  561. ! !
  562. !HLWorkspace methodsFor: 'actions'!
  563. focus
  564. ^ self codeWidget focus
  565. ! !
  566. !HLWorkspace methodsFor: 'rendering'!
  567. renderContentOn: html
  568. html with: (HLContainer with: self codeWidget)
  569. ! !
  570. !HLWorkspace methodsFor: 'testing'!
  571. canHaveFocus
  572. ^ true
  573. ! !
  574. !HLWorkspace class methodsFor: 'accessing'!
  575. tabLabel
  576. ^ 'Workspace'
  577. !
  578. tabPriority
  579. ^ 10
  580. ! !
  581. !HLWorkspace class methodsFor: 'testing'!
  582. canBeOpenAsTab
  583. ^ true
  584. ! !