Browse Source

Fixes #859

- Adds Point >> dist:
- Adds Node >> atPosition: with a test
- Implements CTRL+Click on methods to navigate
Nicolas Petton 10 years ago
parent
commit
d1cb5bd54e

+ 169 - 0
src/Compiler-AST.js

@@ -39,6 +39,94 @@ referencedClasses: []
 }),
 globals.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "allNodes",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var allNodes;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self._nodes();
+$ctx1.sendIdx["nodes"]=1;
+allNodes=_st($1)._asSet();
+_st(self._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(allNodes)._addAll_(_st(each)._allNodes());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
+$2=allNodes;
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"allNodes",{allNodes:allNodes},globals.Node)})},
+args: [],
+source: "allNodes\x0a\x09| allNodes |\x0a\x09\x0a\x09allNodes := self nodes asSet.\x0a\x09self nodes do: [ :each | \x0a\x09\x09allNodes addAll: each allNodes ].\x0a\x09\x0a\x09^ allNodes",
+messageSends: ["asSet", "nodes", "do:", "addAll:", "allNodes"],
+referencedClasses: []
+}),
+globals.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "atPosition:",
+protocol: 'accessing',
+fn: function (aPoint){
+var self=this;
+var children;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$5,$4,$3;
+var $early={};
+try {
+children=_st(self._allNodes())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._inPosition_(aPoint);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
+$1=console;
+$2=_st(children)._asArray();
+$ctx1.sendIdx["asArray"]=1;
+_st($1)._log_($2);
+_st(children)._ifEmpty_((function(){
+throw $early=[nil];
+}));
+$3=_st(_st(_st(children)._asArray())._sort_((function(a,b){
+return smalltalk.withContext(function($ctx2) {
+$5=_st(a)._positionStart();
+$ctx2.sendIdx["positionStart"]=1;
+$4=_st($5)._dist_(aPoint);
+$ctx2.sendIdx["dist:"]=1;
+return _st($4).__lt_eq(_st(_st(b)._positionStart())._dist_(aPoint));
+}, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1,3)})})))._first();
+return $3;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"atPosition:",{aPoint:aPoint,children:children},globals.Node)})},
+args: ["aPoint"],
+source: "atPosition: aPoint\x0a\x09\x22Answer the node in the receiver's tree at aPoint\x22\x0a\x09\x0a\x09| children |\x0a\x09\x0a\x09children := self allNodes select: [ :each | \x0a\x09\x09each inPosition: aPoint ].\x0a\x09\x0a\x09console log: children asArray.\x0a\x09\x0a\x09children ifEmpty: [ ^ nil ].\x0a\x09\x0a\x09^ (children asArray sort: [ :a :b | \x0a\x09\x09(a positionStart dist: aPoint) <= \x0a\x09\x09(b positionStart dist: aPoint) ]) first",
+messageSends: ["select:", "allNodes", "inPosition:", "log:", "asArray", "ifEmpty:", "first", "sort:", "<=", "dist:", "positionStart"],
+referencedClasses: []
+}),
+globals.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "inPosition:",
+protocol: 'testing',
+fn: function (aPoint){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self._positionStart()).__lt_eq(aPoint))._and_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(self._positionEnd()).__gt_eq(aPoint);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"inPosition:",{aPoint:aPoint},globals.Node)})},
+args: ["aPoint"],
+source: "inPosition: aPoint\x0a\x09^ (self positionStart <= aPoint and: [\x0a\x09\x09self positionEnd >= aPoint ])",
+messageSends: ["and:", "<=", "positionStart", ">=", "positionEnd"],
+referencedClasses: []
+}),
+globals.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isAssignmentNode",
@@ -147,6 +235,21 @@ referencedClasses: []
 }),
 globals.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isNavigationNode",
+protocol: 'testing',
+fn: function (){
+var self=this;
+return false;
+},
+args: [],
+source: "isNavigationNode\x0a\x09\x22Answer true if the node can be navigated to\x22\x0a\x09\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+globals.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isNode",
@@ -1742,6 +1845,21 @@ referencedClasses: []
 }),
 globals.SendNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isNavigationNode",
+protocol: 'testing',
+fn: function (){
+var self=this;
+return true;
+},
+args: [],
+source: "isNavigationNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+globals.SendNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isSendNode",
@@ -1757,6 +1875,24 @@ referencedClasses: []
 }),
 globals.SendNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "navigationLink",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self._selector();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"navigationLink",{},globals.SendNode)})},
+args: [],
+source: "navigationLink\x0a\x09^ self selector",
+messageSends: ["selector"],
+referencedClasses: []
+}),
+globals.SendNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "nodes",
@@ -2410,6 +2546,21 @@ referencedClasses: []
 }),
 globals.VariableNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isNavigationNode",
+protocol: 'testing',
+fn: function (){
+var self=this;
+return true;
+},
+args: [],
+source: "isNavigationNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+globals.VariableNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isVariableNode",
@@ -2425,6 +2576,24 @@ referencedClasses: []
 }),
 globals.VariableNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "navigationLink",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"navigationLink",{},globals.VariableNode)})},
+args: [],
+source: "navigationLink\x0a\x09^ self value",
+messageSends: ["value"],
+referencedClasses: []
+}),
+globals.VariableNode);
+
 
 smalltalk.addMethod(
 smalltalk.method({

+ 54 - 0
src/Compiler-AST.st

@@ -16,6 +16,33 @@ addNode: aNode
 	aNode parent: self
 !
 
+allNodes
+	| allNodes |
+	
+	allNodes := self nodes asSet.
+	self nodes do: [ :each | 
+		allNodes addAll: each allNodes ].
+	
+	^ allNodes
+!
+
+atPosition: aPoint
+	"Answer the node in the receiver's tree at aPoint"
+	
+	| children |
+	
+	children := self allNodes select: [ :each | 
+		each inPosition: aPoint ].
+	
+	console log: children asArray.
+	
+	children ifEmpty: [ ^ nil ].
+	
+	^ (children asArray sort: [ :a :b | 
+		(a positionStart dist: aPoint) <= 
+		(b positionStart dist: aPoint) ]) first
+!
+
 method
 	^ self parent ifNotNil: [ :node | node method ]
 !
@@ -122,6 +149,11 @@ postCopy
 
 !Node methodsFor: 'testing'!
 
+inPosition: aPoint
+	^ (self positionStart <= aPoint and: [
+		self positionEnd >= aPoint ])
+!
+
 isAssignmentNode
 	^ false
 !
@@ -150,6 +182,12 @@ isLastChild
 	^ self parent nodes last = self
 !
 
+isNavigationNode
+	"Answer true if the node can be navigated to"
+	
+	^ false
+!
+
 isNode
 	^ true
 !
@@ -530,6 +568,10 @@ index: anInteger
 	index := anInteger
 !
 
+navigationLink
+	^ self selector
+!
+
 nodes
 	self receiver ifNil: [ ^ self arguments copy ].
 	
@@ -582,6 +624,10 @@ isCascadeSendNode
 	^ self parent isCascadeNode
 !
 
+isNavigationNode
+	^ true
+!
+
 isSendNode
 	^ true
 !
@@ -740,6 +786,10 @@ binding
 
 binding: aScopeVar
 	binding := aScopeVar
+!
+
+navigationLink
+	^ self value
 ! !
 
 !VariableNode methodsFor: 'testing'!
@@ -752,6 +802,10 @@ isImmutable
 	^ self binding isImmutable
 !
 
+isNavigationNode
+	^ true
+!
+
 isVariableNode
 	^ true
 ! !

+ 32 - 0
src/Compiler-Tests.js

@@ -269,6 +269,38 @@ globals.ASTPCNodeVisitorTest);
 
 
 
+smalltalk.addClass('ASTPositionTest', globals.ASTParsingTest, [], 'Compiler-Tests');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testAtPosition",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var node;
+return smalltalk.withContext(function($ctx1) { 
+var $3,$4,$2,$1;
+node=self._parse_("yourself\x0a\x09^ self");
+$ctx1.sendIdx["parse:"]=1;
+$3=node;
+$4=(2).__at((4));
+$ctx1.sendIdx["@"]=1;
+$2=_st($3)._atPosition_($4);
+$ctx1.sendIdx["atPosition:"]=1;
+$1=_st($2)._source();
+self._assert_equals_($1,"self");
+$ctx1.sendIdx["assert:equals:"]=1;
+node=self._parse_("foo\x0a\x09true ifTrue: [ 1 ]");
+self._assert_equals_(_st(_st(node)._atPosition_((2).__at((7))))._selector(),"ifTrue:");
+return self}, function($ctx1) {$ctx1.fill(self,"testAtPosition",{node:node},globals.ASTPositionTest)})},
+args: [],
+source: "testAtPosition\x0a\x09| node |\x0a\x09\x0a\x09node := self parse: 'yourself\x0a\x09^ self'.\x0a\x09\x0a\x09self assert: (node atPosition: 2@4) source equals: 'self'.\x0a\x09\x0a\x09node := self parse: 'foo\x0a\x09true ifTrue: [ 1 ]'.\x0a\x09\x0a\x09self assert: (node atPosition: 2@7) selector equals: 'ifTrue:'",
+messageSends: ["parse:", "assert:equals:", "source", "atPosition:", "@", "selector"],
+referencedClasses: []
+}),
+globals.ASTPositionTest);
+
+
+
 smalltalk.addClass('CodeGeneratorTest', globals.ASTParsingTest, ['receiver'], 'Compiler-Tests');
 smalltalk.addMethod(
 smalltalk.method({

+ 20 - 0
src/Compiler-Tests.st

@@ -102,6 +102,26 @@ testPC
 		currentNode) isJSStatementNode
 ! !
 
+ASTParsingTest subclass: #ASTPositionTest
+	instanceVariableNames: ''
+	package: 'Compiler-Tests'!
+
+!ASTPositionTest methodsFor: 'tests'!
+
+testAtPosition
+	| node |
+	
+	node := self parse: 'yourself
+	^ self'.
+	
+	self assert: (node atPosition: 2@4) source equals: 'self'.
+	
+	node := self parse: 'foo
+	true ifTrue: [ 1 ]'.
+	
+	self assert: (node atPosition: 2@7) selector equals: 'ifTrue:'
+! !
+
 ASTParsingTest subclass: #CodeGeneratorTest
 	instanceVariableNames: 'receiver'
 	package: 'Compiler-Tests'!

+ 140 - 4
src/Helios-Workspace.js

@@ -246,18 +246,34 @@ protocol: 'actions',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $1;
+var $1,$2,$3,$4;
 $1=self._editor();
 $ctx1.sendIdx["editor"]=1;
 _st($1)._at_put_("amberCodeWidget",self);
-_st(self._editor())._on_do_("change",(function(){
+$2=self._editor();
+$ctx1.sendIdx["editor"]=2;
+_st($2)._on_do_("change",(function(){
 return smalltalk.withContext(function($ctx2) {
 return self._onChange();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+$ctx1.sendIdx["on:do:"]=1;
+$3=self._editor();
+$ctx1.sendIdx["editor"]=3;
+_st($3)._on_do_("mousedown",(function(cm,event){
+var position,node;
+return smalltalk.withContext(function($ctx2) {
+$4=_st(event)._at_("ctrlKey");
+if(smalltalk.assert($4)){
+position=_st(self._editor())._coordsChar_(globals.HashedCollection._newFromPairs_(["left",_st(event)._clientX(),"top",_st(event)._clientY()]));
+position;
+self._onCtrlClickAt_(_st(_st(_st(position)._line()).__at(_st(position)._ch())).__plus((1)));
+return _st(event)._preventDefault();
+};
+}, function($ctx2) {$ctx2.fillBlock({cm:cm,event:event,position:position,node:node},$ctx1,2)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"configureEditor",{},globals.HLCodeWidget)})},
 args: [],
-source: "configureEditor\x0a\x09self editor at: 'amberCodeWidget' put: self.\x0a\x09self editor on: 'change' do: [ self onChange ]",
-messageSends: ["at:put:", "editor", "on:do:", "onChange"],
+source: "configureEditor\x0a\x09self editor at: 'amberCodeWidget' put: self.\x0a\x09self editor on: 'change' do: [ self onChange ].\x0a\x09self editor on: 'mousedown' do: [ :cm :event | | position node |\x0a\x09\x09(event at: 'ctrlKey') ifTrue: [\x0a\x09\x09\x09position := self editor coordsChar: #{ \x0a\x09\x09\x09\x09'left' -> event clientX.\x0a\x09\x09\x09\x09'top' -> event clientY\x0a\x09\x09\x09}.\x0a\x09\x09\x09self onCtrlClickAt: (position line @ position ch) + 1.\x0a\x09\x09\x09event preventDefault ] ]",
+messageSends: ["at:put:", "editor", "on:do:", "onChange", "ifTrue:", "at:", "coordsChar:", "clientX", "clientY", "onCtrlClickAt:", "+", "@", "line", "ch", "preventDefault"],
 referencedClasses: []
 }),
 globals.HLCodeWidget);
@@ -552,6 +568,69 @@ referencedClasses: []
 }),
 globals.HLCodeWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "navigateTo:",
+protocol: 'actions',
+fn: function (aString){
+var self=this;
+var navigationClass;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+navigationClass=_st(_st(_st(self._model())._environment())._classes())._detect_ifNone_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(each)._name()).__eq(aString);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}),(function(){
+return nil;
+}));
+$1=navigationClass;
+if(($receiver = $1) == nil || $receiver == null){
+self._navigateToReference_(aString);
+} else {
+self._navigateToClass_(navigationClass);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"navigateTo:",{aString:aString,navigationClass:navigationClass},globals.HLCodeWidget)})},
+args: ["aString"],
+source: "navigateTo: aString\x0a\x09| navigationClass |\x0a\x09\x0a\x09navigationClass := self model environment classes \x0a\x09\x09detect: [ :each | each name = aString ]\x0a\x09\x09ifNone: [ nil ].\x0a\x09\x09\x0a\x09navigationClass \x0a\x09\x09ifNil: [ self navigateToReference: aString ]\x0a\x09\x09ifNotNil: [ self navigateToClass: navigationClass ]",
+messageSends: ["detect:ifNone:", "classes", "environment", "model", "=", "name", "ifNil:ifNotNil:", "navigateToReference:", "navigateToClass:"],
+referencedClasses: []
+}),
+globals.HLCodeWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "navigateToClass:",
+protocol: 'actions',
+fn: function (aClass){
+var self=this;
+function $HLBrowser(){return globals.HLBrowser||(typeof HLBrowser=="undefined"?nil:HLBrowser)}
+return smalltalk.withContext(function($ctx1) { 
+_st(_st($HLBrowser())._openAsTab())._browseClass_(aClass);
+return self}, function($ctx1) {$ctx1.fill(self,"navigateToClass:",{aClass:aClass},globals.HLCodeWidget)})},
+args: ["aClass"],
+source: "navigateToClass: aClass\x0a\x09(HLBrowser openAsTab)\x0a\x09\x09browseClass: aClass",
+messageSends: ["browseClass:", "openAsTab"],
+referencedClasses: ["HLBrowser"]
+}),
+globals.HLCodeWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "navigateToReference:",
+protocol: 'actions',
+fn: function (aString){
+var self=this;
+function $HLReferences(){return globals.HLReferences||(typeof HLReferences=="undefined"?nil:HLReferences)}
+return smalltalk.withContext(function($ctx1) { 
+_st(_st($HLReferences())._openAsTab())._search_(aString);
+return self}, function($ctx1) {$ctx1.fill(self,"navigateToReference:",{aString:aString},globals.HLCodeWidget)})},
+args: ["aString"],
+source: "navigateToReference: aString\x0a\x09(HLReferences openAsTab)\x0a\x09\x09search: aString",
+messageSends: ["search:", "openAsTab"],
+referencedClasses: ["HLReferences"]
+}),
+globals.HLCodeWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "onChange",
@@ -568,6 +647,47 @@ referencedClasses: []
 }),
 globals.HLCodeWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "onCtrlClickAt:",
+protocol: 'reactions',
+fn: function (aPosition){
+var self=this;
+var ast,node;
+function $Smalltalk(){return globals.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+function $Error(){return globals.Error||(typeof Error=="undefined"?nil:Error)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+var $early={};
+try {
+ast=_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st($Smalltalk())._parse_(_st(self._editor())._getValue());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}))._on_do_($Error(),(function(error){
+throw $early=[self];
+}));
+node=_st(ast)._atPosition_(aPosition);
+_st(console)._log_(node);
+$1=node;
+if(($receiver = $1) == nil || $receiver == null){
+return self;
+} else {
+$1;
+};
+$2=_st(node)._isNavigationNode();
+if(smalltalk.assert($2)){
+self._navigateTo_(_st(node)._navigationLink());
+};
+return self}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"onCtrlClickAt:",{aPosition:aPosition,ast:ast,node:node},globals.HLCodeWidget)})},
+args: ["aPosition"],
+source: "onCtrlClickAt: aPosition\x0a\x09| ast node |\x0a\x09\x0a\x09ast := [ Smalltalk parse: self editor getValue ] \x0a\x09\x09on: Error \x0a\x09\x09do: [ :error | ^ self ].\x0a\x09\x0a\x09node := (ast atPosition: aPosition).\x0a\x09console log: node.\x0a\x09node ifNil: [ ^ self ].\x0a\x09\x0a\x09node isNavigationNode ifTrue: [ \x0a\x09\x09self navigateTo: node navigationLink ]",
+messageSends: ["on:do:", "parse:", "getValue", "editor", "atPosition:", "log:", "ifNil:", "ifTrue:", "isNavigationNode", "navigateTo:", "navigationLink"],
+referencedClasses: ["Smalltalk", "Error"]
+}),
+globals.HLCodeWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "onDoIt",
@@ -1451,6 +1571,22 @@ referencedClasses: []
 }),
 globals.HLBrowserCodeWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "navigateToClass:",
+protocol: 'actions',
+fn: function (aClass){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._browserModel())._openClassNamed_(_st(aClass)._name());
+return self}, function($ctx1) {$ctx1.fill(self,"navigateToClass:",{aClass:aClass},globals.HLBrowserCodeWidget)})},
+args: ["aClass"],
+source: "navigateToClass: aClass\x0a\x09self browserModel openClassNamed: aClass name",
+messageSends: ["openClassNamed:", "browserModel", "name"],
+referencedClasses: []
+}),
+globals.HLBrowserCodeWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "observeBrowserModel",

+ 50 - 1
src/Helios-Workspace.st

@@ -150,7 +150,15 @@ clear
 
 configureEditor
 	self editor at: 'amberCodeWidget' put: self.
-	self editor on: 'change' do: [ self onChange ]
+	self editor on: 'change' do: [ self onChange ].
+	self editor on: 'mousedown' do: [ :cm :event | | position node |
+		(event at: 'ctrlKey') ifTrue: [
+			position := self editor coordsChar: #{ 
+				'left' -> event clientX.
+				'top' -> event clientY
+			}.
+			self onCtrlClickAt: (position line @ position ch) + 1.
+			event preventDefault ] ]
 !
 
 doIt
@@ -178,6 +186,28 @@ inspectIt
 	self model inspect: self doIt
 !
 
+navigateTo: aString
+	| navigationClass |
+	
+	navigationClass := self model environment classes 
+		detect: [ :each | each name = aString ]
+		ifNone: [ nil ].
+		
+	navigationClass 
+		ifNil: [ self navigateToReference: aString ]
+		ifNotNil: [ self navigateToClass: navigationClass ]
+!
+
+navigateToClass: aClass
+	(HLBrowser openAsTab)
+		browseClass: aClass
+!
+
+navigateToReference: aString
+	(HLReferences openAsTab)
+		search: aString
+!
+
 print: aString
 	| start stop currentLine |
     currentLine := (editor getCursor: false) line.
@@ -244,6 +274,21 @@ onChange
 	self updateState
 !
 
+onCtrlClickAt: aPosition
+	| ast node |
+	
+	ast := [ Smalltalk parse: self editor getValue ] 
+		on: Error 
+		do: [ :error | ^ self ].
+	
+	node := (ast atPosition: aPosition).
+	console log: node.
+	node ifNil: [ ^ self ].
+	
+	node isNavigationNode ifTrue: [ 
+		self navigateTo: node navigationLink ]
+!
+
 onDoIt
 	
 	self doIt
@@ -520,6 +565,10 @@ browserModel: aBrowserModel
 
 !HLBrowserCodeWidget methodsFor: 'actions'!
 
+navigateToClass: aClass
+	self browserModel openClassNamed: aClass name
+!
+
 observeBrowserModel
 	self browserModel announcer
 		on: HLSaveSourceCode

+ 25 - 0
src/Kernel-Objects.js

@@ -3699,6 +3699,31 @@ referencedClasses: []
 }),
 globals.Point);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "dist:",
+protocol: 'transforming',
+fn: function (aPoint){
+var self=this;
+var dx,dy;
+return smalltalk.withContext(function($ctx1) { 
+var $3,$2,$1;
+dx=_st(_st(aPoint)._x()).__minus(self["@x"]);
+$ctx1.sendIdx["-"]=1;
+dy=_st(_st(aPoint)._y()).__minus(self["@y"]);
+$3=_st(dx).__star(dx);
+$ctx1.sendIdx["*"]=1;
+$2=_st($3).__plus(_st(dy).__star(dy));
+$1=_st($2)._sqrt();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"dist:",{aPoint:aPoint,dx:dx,dy:dy},globals.Point)})},
+args: ["aPoint"],
+source: "dist: aPoint \x0a\x09\x22Answer the distance between aPoint and the receiver.\x22\x0a\x09| dx dy |\x0a\x09dx := aPoint x - x.\x0a\x09dy := aPoint y - y.\x0a\x09^ (dx * dx + (dy * dy)) sqrt",
+messageSends: ["-", "x", "y", "sqrt", "+", "*"],
+referencedClasses: []
+}),
+globals.Point);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "printOn:",

+ 8 - 0
src/Kernel-Objects.st

@@ -1218,6 +1218,14 @@ printOn: aStream
 
 !Point methodsFor: 'transforming'!
 
+dist: aPoint 
+	"Answer the distance between aPoint and the receiver."
+	| dx dy |
+	dx := aPoint x - x.
+	dy := aPoint y - y.
+	^ (dx * dx + (dy * dy)) sqrt
+!
+
 translateBy: delta
 	"Answer a Point translated by delta (an instance of Point)."
 	^ (delta x + x) @ (delta y + y)