A snippet expander for Squeak.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

A snippet expander for Squeak.

Matthew 888
Hello!

As a beginner, I am trying to learn Squeak/Smalltalk by implementing a way for
to insert unicode characters by replacing targets in a string before the cursor
when needed while writing in, for example, a Workspace.

Let's say that you write in a Workspace these characters: '->' then you hit
ALT-TAB and '->' get replaced by the character: '→'. How to implemente such a
thing?

I picture a thing called a "Morph" (I guess?) that holds the string that I'm
updating by typing on my keyboard (like a Workspace).  This morph logs things
that happens to it in an output stream of events.  An object called:
"SnippetExpander" reads this stream of event and when appropriate asks the morph
more information (e.g. what are the last N characters before the cursor).
Depeding on the reply, the SnippetExpander asks the morph to update the string.

I imagine messages, observers and streams behaving like in http://reactivex.io.

Here are a few questions that I stumbled upon:

  SnippetExpander tries to observe whatever object has focus.
      What does it mean to "have" focus?
      How to know what has focus?
      What does it mean to "observe"?
     
  When an object has focus and is the type of object you can type text in (let's
  name that thing: focusedTextObject) then SnippetExpander observes its output
  messages.
      How to test an object and know if you can type text in it?
 
  When SnippetExpander observes that the ALT-TAB key has been entered, then
  SnippetExpander asks the focusedTextObject the last N characters before point.
 
  If needed, SnippetExpander asks focusedTextObject to replace the last N
  characters by a replacement string.


Am I doing it wrong?

Bellow, the code of SnippetExpander wrote so far that I "filedOut".

Thx!

====

888-SnippetExpander.st
======================

Object subclass: #SnippetExpander
instanceVariableNames: 'snippetsAndExpansions'
classVariableNames: ''
poolDictionaries: ''
category: '888-SnippetExpander'!
!SnippetExpander commentStamp: '888 6/2/2017 17:03' prior: 0!
I check last characters before the cursor and decide to replace them by an other sequence of characters.!


!SnippetExpander methodsFor: 'behavior' stamp: '888 6/3/2017 01:13'!
expandSnippet: s
^ [(snippetsAndExpansions at: s) at: 2] 
on: KeyNotFound
do: [ :exception |
Transcript
open;
show: 'I could not find a snippet: `', s, '` to expand.';
cr.
].! !

!SnippetExpander methodsFor: 'behavior' stamp: '888 6/3/2017 01:48'!
forgetExpansion: e
(snippetsAndExpansions select: [ :v | (v at: 2) = e ]) keysDo: [ :key | snippetsAndExpansions removeKey: key ].! !

!SnippetExpander methodsFor: 'behavior' stamp: '888 6/3/2017 01:34'!
forgetSnippet: s
snippetsAndExpansions removeKey: s ifAbsent: [ ^ self ].! !

!SnippetExpander methodsFor: 'behavior' stamp: '888 6/3/2017 01:23'!
learnSnippet: s expansion: e
"The next time the last characters behind the cursor match s, replace the match by the expansion e."
snippetsAndExpansions at: s ifPresent: [ :value |
Transcript
open;
show: 'Snippet ', s, ' is already associated to the expansion: ', (value at: 2);
cr;
show: 'A snippet cannot be ambiguous.';
cr.
].
snippetsAndExpansions add: (Array braceWith: s with: e).
! !


!SnippetExpander methodsFor: 'initialization' stamp: '888 6/3/2017 00:27'!
initialize
super initialize.
snippetsAndExpansions := KeyedSet new.
snippetsAndExpansions keyBlock: [ :e | e at: 1].
! !


TestCase subclass: #SnippetExpanderTest
instanceVariableNames: 'snippetExpander'
classVariableNames: ''
poolDictionaries: ''
category: '888-SnippetExpander'!

!SnippetExpanderTest methodsFor: 'as yet unclassified' stamp: '888 6/2/2017 22:49'!
setUp
snippetExpander := SnippetExpander new.! !

!SnippetExpanderTest methodsFor: 'as yet unclassified' stamp: '888 6/3/2017 01:50'!
testBehavior
"self run: #testBehavior"
| t |
snippetExpander learnSnippet: 'a' expansion: 'b'.
self assert: (snippetExpander expandSnippet: 'a') = 'b'.
snippetExpander forgetSnippet: 'a'.
t := snippetExpander expandSnippet: 'a'.
self assert: (t isMemberOf: TranscriptStream).
t closeAllViews.
snippetExpander learnSnippet: 'a' expansion: 'b'.
snippetExpander learnSnippet: 'c' expansion: 'b'.
snippetExpander forgetExpansion: 'b'.
t := snippetExpander expandSnippet: 'a'.
self assert: (t isMemberOf: TranscriptStream).
t := snippetExpander expandSnippet: 'b'.
self assert: (t isMemberOf: TranscriptStream).
t closeAllViews.! !


Object subclass: #UniqueSnippetExpander
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: '888-SnippetExpander'!
!UniqueSnippetExpander commentStamp: '888 6/3/2017 11:50' prior: 0!
I hold the unique instance of SnippetExpander in use, outside of tests, to have only one collection of snippets to expansions associations.!


"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

UniqueSnippetExpander class
instanceVariableNames: 'uniqueInstance'!

!UniqueSnippetExpander class methodsFor: 'as yet unclassified' stamp: '888 6/3/2017 01:55'!
uniqueInstance
uniqueInstance ifNil: [ uniqueInstance := SnippetExpander new. ].
^ uniqueInstance.! !





_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: A snippet expander for Squeak.

Milan Vavra
> Hello!
>
> As a beginner, I am trying to learn Squeak/Smalltalk by implementing a way for
> to insert unicode characters by replacing targets in a string before the cursor
> when needed while writing in, for example, a Workspace.

You can do it, but many small details are involved. That makes it
complicated. Not difficult but complicated. Time consuming. How much
time and effort are you willing to spend to figure it all out?


> Let's say that you write in a Workspace these characters: '->' then you hit
> ALT-TAB and '->' get replaced by the character: '→'. How to implemente such a
> thing?

The shortcuts you could use are Alt-key and Shift-Alt-key. Many are
taken, but you can find a few unused or re-assign those you do not use.

Have a look in:
----
TextEditor class>>initializeCmdKeyShortcuts
SmalltalkEditor class>>initializeCmdKeyShortcuts
TextEditor class>>initializeShiftCmdKeyShortcuts
SmalltalkEditor class>>initializeShiftCmdKeyShortcuts
----

> I picture a thing called a "Morph" (I guess?) that holds the string that I'm
> updating by typing on my keyboard (like a Workspace).

Things are more complicated. The Workspace window has a model, a
Workspace. The model holds the string. The window is a Morph subclass.

> This morph logs things that happens to it in an output stream of events.
> An object called: "SnippetExpander" reads this stream of event and when
> appropriate asks the morph more information (e.g. what are the last N
> characters before the cursor). Depeding on the reply, the
> SnippetExpander asks the morph to update the string.
>
> I imagine messages, observers and streams behaving like in http://reactivex.io.

I am not sure this is how things work.

Where did you get the idea?

What you could do instead is when you press you chosen keyboard
shortcut, and the method handling it gets called, you ask the text
editor where the cursor position is. Then get a string a few characters
back from that. Compare it with all those strings you want to replace.
If you find a match, replace it and finally refresh the text editor so
that the change you have made becomes visible. A lot of tedious work.
Because you have to figure out what gets called when. What is stored
where. What is passed where. Do some debugging. See a few 'big red Xes'
or image hangs if things go wrong. Rinse and repeat. You can learn a lot
about how things are connected. Are you up for the challenge?


> Here are a few questions that I stumbled upon:
>
> SnippetExpander tries to observe whatever object has focus.
> What does it mean to "have" focus?
> How to know what has focus?
> What does it mean to "observe"?
>
> When an object has focus and is the type of object you can type text in (let's
> name that thing: focusedTextObject) then SnippetExpander observes its output
> messages.
> How to test an object and know if you can type text in it?
>
> When SnippetExpander observes that the ALT-TAB key has been entered, then
> SnippetExpander asks the focusedTextObject the last N characters before point.
>
> If needed, SnippetExpander asks focusedTextObject to replace the last N
> characters by a replacement string.
>
> Am I doing it wrong?
>
> Bellow, the code of SnippetExpander wrote so far that I "filedOut".

The way you posted it all the leading whitespace is lost. You could try
zipping it first, that way it would stay the way you filed it out with
all that leading whitespace (and the weird CR line endings that Squeak
uses) intact.
 
> Thx!

Best Regards,

Milan Vavra