Helios-Workspace.st 13 KB

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