Helios-Workspace.st 16 KB

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