| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807 | Smalltalk current createPackage: 'Helios-Workspace'!Object subclass: #HLCodeModel	instanceVariableNames: 'announcer environment receiver'	package: 'Helios-Workspace'!!HLCodeModel methodsFor: 'accessing'!announcer	^ announcer ifNil: [ announcer := Announcer new ]!environment	^ environment ifNil: [ HLManager current environment ]!environment: anEnvironment	environment := anEnvironment!receiver	^ receiver ifNil: [ receiver := self defaultReceiver ]!receiver: anObject	receiver := anObject! !!HLCodeModel methodsFor: 'actions'!doIt: aString	^ self environment eval: aString on: self receiver!inspect: anObject	self environment inspect: anObject! !!HLCodeModel methodsFor: 'defaults'!defaultReceiver	^ self environment doItReceiver! !!HLCodeModel class methodsFor: 'actions'!on: anEnvironment	^ self new    	environment: anEnvironment;        yourself! !HLWidget subclass: #HLCodeWidget	instanceVariableNames: 'model wrapper code editor state'	package: 'Helios-Workspace'!!HLCodeWidget methodsFor: 'accessing'!announcer	^ self model announcer!contents	^ editor getValue!contents: aString	editor setValue: aString.	state ifNotNil: [ self updateState ]!currentLine    ^editor getLine: (editor getCursor line)!currentLineOrSelection    ^editor somethingSelected		ifFalse: [ self currentLine ]		ifTrue: [ self selection ]!editorOptions	^ #{		'theme' -> 'amber'.        'lineNumbers' -> true.        'enterMode' -> 'flat'.        'indentWithTabs' -> true.		'indentUnit' -> 4.        'matchBrackets' -> true.        'electricChars' -> false.		'keyMap' -> 'Amber'.		'extraKeys' -> #{'Shift-Space' -> 'autocomplete'}	}!model	^ model ifNil: [ model := HLCodeModel new ]!model: aModel	model := aModel!receiver	^ self model receiver!receiver: anObject	self model receiver: anObject!selection	^editor getSelection!selectionEnd   ^code element selectionEnd!selectionEnd: anInteger   code element selectionEnd: anInteger!selectionStart   ^code element selectionStart!selectionStart: anInteger   code element selectionStart: anInteger! !!HLCodeWidget methodsFor: 'actions'!clear	self contents: ''!configureEditor	self editor at: 'amberCodeWidget' put: self.	self editor on: 'change' do: [ self onChange ]!doIt	| result |	self model announcer announce: (HLDoItRequested on: model).	result := model doIt: self currentLineOrSelection.	self model announcer announce: (HLDoItExecuted on: model).	^ result!editor	^ editor!focus	editor focus!inspectIt	| newInspector |       	self model announcer announce: (HLInspectItRequested on: model).	self model inspect: self doIt!print: aString	| start stop currentLine |    currentLine := (editor getCursor: false) line.	start := HashedCollection new.	start at: 'line' put: currentLine.	start at: 'ch' put: (editor getCursor: false) ch.    (editor getSelection) ifEmpty: [    	"select current line if selection is empty"    	start at: 'ch' put: (editor getLine: currentLine) size.        editor setSelection: #{'line' -> currentLine. 'ch' -> 0} end: start.    ].	stop := HashedCollection new.	stop at: 'line' put: currentLine.	stop at: 'ch' put: ((start at: 'ch') + aString size + 2).	editor replaceSelection: (editor getSelection, ' ', aString, ' ').	editor setCursor: (editor getCursor: true).	editor setSelection: stop end: start!printIt	| result |	result := self doIt.       	self model announcer announce: (HLPrintItRequested on: model).	self print: result printString.		self focus.!saveIt	"I do not do anything"!setEditorOn: aTextarea	<self['@editor'] = CodeMirror.fromTextArea(aTextarea, self._editorOptions())>! !!HLCodeWidget methodsFor: 'hints'!messageHintFor: anEditor token: aToken	^ ((Smalltalk current at: 'allSelectors') value asArray 		select: [ :each | each includesSubString: aToken string ])		reject: [ :each | each = aToken string ]!variableHintFor: anEditor token: aToken	| variables classNames pseudoVariables |		variables := (anEditor display wrapper asJQuery find: 'span.cm-variable') get		collect: [ :each | each asJQuery html ].		classNames := Smalltalk current classes collect: [ :each | each name ].	pseudoVariables := Smalltalk current pseudoVariableNames.		^ ((variables, classNames, pseudoVariables) asSet asArray 		select: [ :each | each includesSubString: aToken string ])		reject: [ :each | each = aToken string ]! !!HLCodeWidget methodsFor: 'reactions'!onChange	self updateState!onDoIt		self doIt!onInspectIt	self inspectIt!onPrintIt	self printIt!onSaveIt	"I do not do anything"! !!HLCodeWidget methodsFor: 'rendering'!renderButtonsOn: html	html button 		class: 'button';		with: 'DoIt';		onClick: [ self doIt ].	html button 		class: 'button';		with: 'PrintIt';		onClick: [ self printIt ].	html button 		class: 'button';		with: 'InspectIt';		onClick: [ self inspectIt ]!renderContentOn: html	html div class: 'editor'; with: [		code := html textarea ].	state := html div class: 'state'.		html div 		class: 'buttons_bar';		with: [ self renderButtonsOn: html ].		self 		setEditorOn: code element;		configureEditor;		updateState! !!HLCodeWidget methodsFor: 'testing'!canHaveFocus	^ true!hasFocus	^ code asJQuery is: ':active'!hasModification	^ false! !!HLCodeWidget methodsFor: 'updating'!updateState	self hasModification 		ifTrue: [ state asJQuery addClass: 'modified' ]		ifFalse: [ state asJQuery removeClass: 'modified' ]! !!HLCodeWidget class methodsFor: 'accessing'!keyMap	^ HLManager current keyBinder systemIsMac		ifTrue: [ self macKeyMap ]		ifFalse: [ self pcKeyMap ]!macKeyMap	^ #{		'Alt-Backspace'		-> 'delWordBefore'.		'Alt-Delete'		-> 'delWordAfter'. 		'Alt-Left'		-> 'goWordBoundaryLeft'.		'Alt-Right'		-> 'goWordBoundaryRight'. 		'Cmd-A'			-> 'selectAll'. 		'Cmd-Alt-F'		-> 'replace'. 		'Cmd-D'			-> 'doIt'. 		'Cmd-Down'		-> 'goDocEnd'. 		'Cmd-End'		-> 'goDocEnd'. 		'Cmd-F'			-> 'find'.		'Cmd-G'			-> 'findNext'. 		'Cmd-I'			-> 'inspectIt'. 		'Cmd-Left'		-> 'goLineStart'. 		'Cmd-P'			-> 'printIt'. 		'Cmd-Right'		-> 'goLineEnd'. 		'Cmd-S'			-> 'saveIt'. 		'Cmd-Up'		-> 'goDocStart'. 		'Cmd-Y'			-> 'redo'.		'Cmd-Z'			-> 'undo'. 		'Cmd-['			-> 'indentLess'. 		'Cmd-]'			-> 'indentMore'.		'Ctrl-Alt-Backspace'	-> 'delWordAfter'. 		'Shift-Cmd-Alt-F'	-> 'replaceAll'.		'Shift-Cmd-G'		-> 'findPrev'. 		'Shift-Cmd-Z'		-> 'redo'.     	'fallthrough' 	-> { 'basic'. 'emacsy' }  }!pcKeyMap	^ #{		'Alt-Left' -> 'goLineStart'. 		'Alt-Right' -> 'goLineEnd'.		'Alt-Up' -> 'goDocStart'. 		'Ctrl-A' -> 'selectAll'. 		'Ctrl-Backspace' -> 'delWordBefore'. 		'Ctrl-D' -> 'doIt'. 		'Ctrl-Delete' -> 'delWordAfter'. 		'Ctrl-Down' -> 'goDocEnd'.		'Ctrl-End' -> 'goDocEnd'. 		'Ctrl-F' -> 'find'.		'Ctrl-G' -> 'findNext'. 		'Ctrl-I' -> 'inspectIt'.		'Ctrl-Home' -> 'goDocStart'. 		'Ctrl-Left' -> 'goWordBoundaryLeft'. 		'Ctrl-P' -> 'printIt'.		'Ctrl-Right' -> 'goWordBoundaryRight'. 		'Ctrl-S' -> 'saveIt'. 		'Ctrl-Y' -> 'redo'.		'Ctrl-Z' -> 'undo'. 		'Ctrl-[' -> 'indentLess'. 		'Ctrl-]' -> 'indentMore'.		'Shift-Ctrl-F' -> 'replace'. 		'Shift-Ctrl-G' -> 'findPrev'. 		'Shift-Ctrl-R' -> 'replaceAll'.		'Shift-Ctrl-Z' -> 'redo'. 		'fallthrough' -> #('basic')}! !!HLCodeWidget class methodsFor: 'hints'!hintFor: anEditor options: options	| cursor token completions |		cursor := anEditor getCursor.	token := anEditor getTokenAt: cursor.	token at: 'state' put: ((CodeMirror basicAt: 'innerMode')		value: anEditor getMode value: (token at: 'state')) state.		completions := token type = 'variable' 		ifTrue: [ HLCodeWidget variableHintFor: anEditor token: token ]		ifFalse: [ HLCodeWidget messageHintFor: anEditor token: token ].		^ #{		'list' -> completions.		'from' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token end).		'to' -> ((CodeMirror basicAt: 'Pos') value: cursor line value: token start)	}!messageHintFor: anEditor token: aToken	^ (anEditor at: 'amberCodeWidget')		messageHintFor: anEditor token: aToken!variableHintFor: anEditor token: aToken	^ (anEditor at: 'amberCodeWidget')		variableHintFor: anEditor token: aToken! !!HLCodeWidget class methodsFor: 'initialization'!initialize	super initialize.	self 		setupCodeMirror;		setupCommands;		setupKeyMaps.!setupCodeMirror	< 		CodeMirror.keyMap.default.fallthrough = ["basic"];		CodeMirror.commands.autocomplete = function(cm) {			CodeMirror.showHint(cm, self._hintFor_options_);		}	>!setupCommands	(CodeMirror basicAt: 'commands') 		at: 'doIt' put: [ :cm | cm amberCodeWidget doIt ];		at: 'inspectIt' put: [ :cm | cm amberCodeWidget inspectIt ];		at: 'printIt' put: [ :cm | cm amberCodeWidget printIt ];		at: 'saveIt' put: [ :cm | cm amberCodeWidget saveIt ]!setupKeyMaps	<CodeMirror.keyMap['Amber'] = self._keyMap()>! !HLCodeWidget subclass: #HLNavigationCodeWidget	instanceVariableNames: 'methodContents'	package: 'Helios-Workspace'!!HLNavigationCodeWidget methodsFor: 'accessing'!configureEditor	super configureEditor.	self contents: self methodContents!contents: aString	self methodContents: aString.	super contents: aString!methodContents	^ methodContents ifNil: [ '' ]!methodContents: aString	^ methodContents := aString!previous	"for browser lists widget"!previous: aWidget	"for browser lists widget"! !!HLNavigationCodeWidget methodsFor: 'testing'!hasModification	^ (self methodContents = self contents) not! !!HLNavigationCodeWidget class methodsFor: 'instance creation'!on: aBrowserModel	^ self new		browserModel: aBrowserModel;		yourself! !!HLNavigationCodeWidget class methodsFor: 'testing'!canBeOpenAsTab	^ false! !HLNavigationCodeWidget subclass: #HLBrowserCodeWidget	instanceVariableNames: 'browserModel'	package: 'Helios-Workspace'!!HLBrowserCodeWidget methodsFor: 'accessing'!browserModel	^ browserModel!browserModel: aBrowserModel	browserModel := aBrowserModel.	self 		observeSystem;		observeBrowserModel! !!HLBrowserCodeWidget methodsFor: 'actions'!observeBrowserModel	self browserModel announcer		on: HLSaveSourceCode		send: #onSaveIt		to: self;				on: HLShowInstanceToggled		send: #onShowInstanceToggled		to: self;				on: HLSourceCodeSaved		send: #onSourceCodeSaved		to: self;				on: HLAboutToChange		send: #onBrowserAboutToChange:		to: self;				on: HLParseErrorRaised		send: #onParseError:		to: self;				on: HLCompileErrorRaised		send: #onCompileError:		to: self;				on: HLUnknownVariableErrorRaised		send: #onUnknownVariableError:		to: self;				on: HLInstVarAdded 		send: #onInstVarAdded		to: self;				on: HLMethodSelected 		send: #onMethodSelected:		to: self;		    	on: HLClassSelected 		send: #onClassSelected:		to: self;				on: HLPackageSelected 		send: #onPackageSelected:		to: self;		    	on: HLProtocolSelected 		send: #onProtocolSelected: 		to: self;				on: HLSourceCodeFocusRequested 		send: #onSourceCodeFocusRequested		to: self!observeSystem	self browserModel systemAnnouncer    	on: MethodModified        send: #onMethodModified:		to: self!refresh	self hasModification ifTrue: [ ^ self ].    self hasFocus ifTrue: [ ^ self ].	self contents: self browserModel selectedMethod source!renderButtonsOn: html	html button 		class: 'button';		with: 'SaveIt';		onClick: [ self saveIt ].	super renderButtonsOn: html!saveIt	self browserModel saveSourceCode!unregister	super unregsiter.		self browserModel announcer unsubscribe: self.	self browserModel systemAnnouncer unsubscribe: self! !!HLBrowserCodeWidget methodsFor: 'reactions'!onBrowserAboutToChange: anAnnouncement	| block |		block := anAnnouncement actionBlock.		self hasModification		ifTrue: [			self 				confirm: 'Changes have not been saved. Do you want to discard these changes?' 				ifTrue: [					"Don't ask twice"					self methodContents: self contents.					block value ].									HLChangeForbidden signal ]!onClassSelected: anAnnouncement	| class |		class:= anAnnouncement item.		class ifNil: [ ^ self contents: '' ].	self contents: class definition!onCompileError: anAnnouncement	self alert: anAnnouncement error messageText!onInstVarAdded	self browserModel save: self contents!onMethodModified: anAnnouncement	| method |		method := anAnnouncement method.		self browserModel selectedClass = method methodClass ifFalse: [ ^ self ].	self browserModel selectedMethod ifNil: [ ^ self ].	self browserModel selectedMethod selector = method selector ifFalse: [ ^ self ].	self refresh!onMethodSelected: anAnnouncement	| method |		method := anAnnouncement item.		method ifNil: [ ^ self contents: '' ].	self contents: method source!onPackageSelected: anAnnouncement	| package |		package := anAnnouncement item.		package ifNil: [ ^ self contents: '' ].	self contents: package definition!onParseError: anAnnouncement	| lineIndex newContents |		lineIndex := 1.		self contents: (String streamContents: [ :stream |		self contents linesDo: [ :each |			lineIndex = anAnnouncement line 				ifTrue: [ 					stream 						nextPutAll: (each copyFrom: 1 to: anAnnouncement column);						nextPutAll: '<- ';						nextPutAll: anAnnouncement message;						nextPutAll: ' ';						nextPutAll: (each copyFrom: anAnnouncement column + 1 to: each size) ]				ifFalse: [ stream nextPutAll: each ].			stream nextPutAll: String cr.			lineIndex := lineIndex + 1 ] ])!onProtocolSelected: anAnnouncement	self browserModel selectedClass ifNil: [ ^ self contents: '' ].	self contents: self browserModel selectedClass definition!onSaveIt	self browserModel save: self contents!onShowInstanceToggled	self browserModel selectedClass ifNil: [ ^ self contents: '' ].    	self contents: self browserModel selectedClass definition!onSourceCodeFocusRequested	self focus!onSourceCodeSaved	self methodContents: self contents.	self updateState!onUnknownVariableError: anAnnouncement	| error |		error := anAnnouncement error.		self 		confirm: (String streamContents: [ :stream |			stream 				nextPutAll: error messageText;				nextPutAll: String cr;				nextPutAll: 'Would you like to define an instance variable?' ])		ifTrue: [			self browserModel addInstVarNamed: error variableName ]! !!HLBrowserCodeWidget class methodsFor: 'instance creation'!on: aBrowserModel	^ self new		browserModel: aBrowserModel;		yourself! !!HLBrowserCodeWidget class methodsFor: 'testing'!canBeOpenAsTab	^ false! !HLWidget subclass: #HLWorkspace	instanceVariableNames: 'codeWidget transcript'	package: 'Helios-Workspace'!!HLWorkspace methodsFor: 'accessing'!codeWidget	^ codeWidget ifNil: [ codeWidget := HLCodeWidget new ]!transcript	^ transcript ifNil: [ transcript := HLTranscript new ]! !!HLWorkspace methodsFor: 'actions'!focus	^ self codeWidget focus!unregister	super unregister.	self transcript unregister! !!HLWorkspace methodsFor: 'rendering'!renderContentOn: html	html with: (HLContainer with: (HLHorizontalSplitter		with: self codeWidget		with: [ :canvas | self renderTranscriptOn: canvas ]))!renderTranscriptOn: html	html div 		class: 'transcript-container';		with: [			html div				class: 'list-label';				with: 'Transcript'.			self transcript renderOn: html ]! !!HLWorkspace methodsFor: 'testing'!canHaveFocus	^ true! !!HLWorkspace class methodsFor: 'accessing'!tabClass	^ 'workspace'!tabLabel	^ 'Workspace'!tabPriority	^ 10! !!HLWorkspace class methodsFor: 'testing'!canBeOpenAsTab	^ true! !
 |