[Newbies] A snippet expander for Squeak.

Matthew 888 matthew888 at protonmail.com
Sat Jun 3 14:50:17 UTC 2017


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.! !
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/beginners/attachments/20170603/205da027/attachment.html>


More information about the Beginners mailing list