diff --git a/src/HeuristicCompletion-Model/CoBeginsWithFilter.class.st b/src/HeuristicCompletion-Model/CoBeginsWithFilter.class.st index 318eb0981b5..47d852b9281 100644 --- a/src/HeuristicCompletion-Model/CoBeginsWithFilter.class.st +++ b/src/HeuristicCompletion-Model/CoBeginsWithFilter.class.st @@ -26,6 +26,23 @@ CoBeginsWithFilter class >> caseSensitive: aBoolean filterString: aString [ yourself ] ] +{ #category : 'matching' } +CoBeginsWithFilter >> candidate: aCandidate matchesCamelCaseCaseSensitive: aBoolean [ + + ^ (aCandidate respondsTo: #matchesCamelCaseCompletionToken:caseSensitive:) + and: [ aCandidate + matchesCamelCaseCompletionToken: completionString + caseSensitive: aBoolean ] +] + +{ #category : 'matching' } +CoBeginsWithFilter >> candidate: aCandidate matchesPrefixCaseSensitive: aBoolean [ + + ^ (aCandidate respondsTo: #matchesPrefixToken:caseSensitive:) + ifTrue: [ aCandidate matchesPrefixToken: completionString caseSensitive: aBoolean ] + ifFalse: [ aCandidate asString beginsWith: completionString caseSensitive: aBoolean ] +] + { #category : 'accessing' } CoBeginsWithFilter >> completionString [ diff --git a/src/HeuristicCompletion-Model/CoCaseInsensitiveBeginsWithFilter.class.st b/src/HeuristicCompletion-Model/CoCaseInsensitiveBeginsWithFilter.class.st index 0a98a762542..d1a955241e7 100644 --- a/src/HeuristicCompletion-Model/CoCaseInsensitiveBeginsWithFilter.class.st +++ b/src/HeuristicCompletion-Model/CoCaseInsensitiveBeginsWithFilter.class.st @@ -22,7 +22,9 @@ CoCaseInsensitiveBeginsWithFilter class >> filterString: aString [ { #category : 'testing' } CoCaseInsensitiveBeginsWithFilter >> accepts: aCandidate [ - ^ aCandidate contents asLowercase beginsWith: completionString asLowercase + ^ (self candidate: aCandidate matchesPrefixCaseSensitive: false) + or: [ NECPreferences camelCaseGlobalMatching + and: [ self candidate: aCandidate matchesCamelCaseCaseSensitive: false ] ] ] { #category : 'testing' } diff --git a/src/HeuristicCompletion-Model/CoCaseSensitiveBeginsWithFilter.class.st b/src/HeuristicCompletion-Model/CoCaseSensitiveBeginsWithFilter.class.st index 5b8765540eb..7251ee0992e 100644 --- a/src/HeuristicCompletion-Model/CoCaseSensitiveBeginsWithFilter.class.st +++ b/src/HeuristicCompletion-Model/CoCaseSensitiveBeginsWithFilter.class.st @@ -22,7 +22,9 @@ CoCaseSensitiveBeginsWithFilter class >> filterString: aString [ { #category : 'testing' } CoCaseSensitiveBeginsWithFilter >> accepts: aCandidate [ - ^ aCandidate contents beginsWith: completionString + ^ (self candidate: aCandidate matchesPrefixCaseSensitive: true) + or: [ NECPreferences camelCaseGlobalMatching + and: [ self candidate: aCandidate matchesCamelCaseCaseSensitive: true ] ] ] { #category : 'testing' } diff --git a/src/HeuristicCompletion-Model/CoGlobalVariableFetcher.class.st b/src/HeuristicCompletion-Model/CoGlobalVariableFetcher.class.st index be7ecbe6208..e30d8129dd1 100644 --- a/src/HeuristicCompletion-Model/CoGlobalVariableFetcher.class.st +++ b/src/HeuristicCompletion-Model/CoGlobalVariableFetcher.class.st @@ -18,10 +18,29 @@ CoGlobalVariableFetcher >> completionClass: aClass [ { #category : 'enumerating' } CoGlobalVariableFetcher >> entriesDo: aBlock [ + NECPreferences camelCaseGlobalMatching + ifTrue: [ ^ self entriesDoWithFullEnvironmentScan: aBlock ]. + self systemNavigation allGlobalNamesStartingWith: filter completionString - do: [ :e | aBlock value: - ((NECGlobalEntry contents: e node: astNode) - fetcher: self; yourself) ] - caseSensitive: (NECPreferences caseSensitive) + do: [ :e | aBlock value: (self newEntryFor: e) ] + caseSensitive: NECPreferences caseSensitive + +] + +{ #category : 'enumerating' } +CoGlobalVariableFetcher >> entriesDoWithFullEnvironmentScan: aBlock [ + + self systemNavigation + allGlobalNamesStartingWith: '' + do: [ :e | aBlock value: (self newEntryFor: e) ] + caseSensitive: true +] + +{ #category : 'private' } +CoGlobalVariableFetcher >> newEntryFor: aGlobalName [ + + ^ (NECGlobalEntry contents: aGlobalName node: astNode) + fetcher: self; + yourself ] diff --git a/src/NECompletion-Preferences/NECPreferences.class.st b/src/NECompletion-Preferences/NECPreferences.class.st index 8dd07274be8..c60b8668bf9 100644 --- a/src/NECompletion-Preferences/NECPreferences.class.st +++ b/src/NECompletion-Preferences/NECPreferences.class.st @@ -20,7 +20,8 @@ Class { 'popupShowWithShortcut', 'smartCharactersWithSingleSpace', 'smartCharactersWithDoubleSpace', - 'showCompletionDetails' + 'showCompletionDetails', + 'camelCaseGlobalMatching' ], #category : 'NECompletion-Preferences', #package : 'NECompletion-Preferences' @@ -51,6 +52,17 @@ NECPreferences class >> backgroundColor: aColor [ backgroundColor := aColor ] +{ #category : 'accessing' } +NECPreferences class >> camelCaseGlobalMatching [ + ^ camelCaseGlobalMatching +] + +{ #category : 'accessing' } +NECPreferences class >> camelCaseGlobalMatching: aBoolean [ + + camelCaseGlobalMatching := aBoolean +] + { #category : 'accessing' } NECPreferences class >> caseSensitive [ ^ caseSensitive @@ -66,6 +78,12 @@ NECPreferences class >> defaultBackgroundColor [ ^ self theme menuColor ] +{ #category : 'defaults' } +NECPreferences class >> defaultCamelCaseGlobalMatching [ + + ^ false +] + { #category : 'defaults' } NECPreferences class >> defaultPopupDelay [ ^ 200 @@ -141,6 +159,7 @@ NECPreferences class >> initialize [ popupShowAutomatic := self defaultPopupShowAutomatic. popupAutomaticDelay := self defaultPopupDelay. showCompletionDetails := self defaultShowCompletionDetails. + camelCaseGlobalMatching := self defaultCamelCaseGlobalMatching. NECEntry spaceAfterCompletion: true ] @@ -228,6 +247,10 @@ NECPreferences class >> settingsOn: aBuilder [ label: 'Smart Characters'; default: true; description: 'Decide if you want eCompletion to use smart characters, e.g, to automatically close brackets.'. + (aBuilder setting: #camelCaseGlobalMatching) + label: 'Camel-case global matching'; + default: self defaultCamelCaseGlobalMatching; + description: 'Allow global and class completions to match camel-case abbreviations such as SC for SortedCollection.' . (aBuilder setting: #smartCharactersWithSingleSpace) label: 'Smart Characters with Single Space'; default: ''; diff --git a/src/NECompletion/NECEntry.class.st b/src/NECompletion/NECEntry.class.st index 3b6c0327a8a..51691976997 100644 --- a/src/NECompletion/NECEntry.class.st +++ b/src/NECompletion/NECEntry.class.st @@ -68,7 +68,7 @@ NECEntry >> activateOn: aCompletionContext [ { #category : 'matching' } NECEntry >> beginsWith: aString [ - ^ self contents beginsWith: aString + ^ self matchesPrefixToken: aString caseSensitive: true ] { #category : 'private' } @@ -147,6 +147,18 @@ NECEntry >> label [ ^ typeString ] +{ #category : 'matching' } +NECEntry >> matchesCamelCaseCompletionToken: aString caseSensitive: aBoolean [ + + ^ false +] + +{ #category : 'matching' } +NECEntry >> matchesPrefixToken: aString caseSensitive: aBoolean [ + + ^ self contents beginsWith: aString caseSensitive: aBoolean +] + { #category : 'accessing' } NECEntry >> node [ diff --git a/src/NECompletion/NECGlobalEntry.class.st b/src/NECompletion/NECGlobalEntry.class.st index 03a263eb344..b31b75a5c49 100644 --- a/src/NECompletion/NECGlobalEntry.class.st +++ b/src/NECompletion/NECGlobalEntry.class.st @@ -9,11 +9,83 @@ Class { #tag : 'Model' } +{ #category : 'matching' } +NECGlobalEntry >> camelCasePartsOf: aString [ + + | parts current | + parts := OrderedCollection new. + current := WriteStream on: String new. + + aString doWithIndex: [ :ch :index | + (index > 1 and: [ + ch isUppercase and: [ current contents notEmpty ] ]) + ifTrue: [ + parts add: current contents. + current := WriteStream on: String new ]. + current nextPut: ch ]. + + current contents ifNotEmpty: [ parts add: current contents ]. + ^ parts asArray +] + { #category : 'accessing' } NECGlobalEntry >> hightlightSymbol [ ^ #globalVar ] +{ #category : 'matching' } +NECGlobalEntry >> matchToken: token againstParts: parts [ + + ^ self + matchToken: token + againstParts: parts + partIndex: 1 +] + +{ #category : 'matching' } +NECGlobalEntry >> matchToken: token againstParts: parts partIndex: startIndex [ + + token ifEmpty: [ ^ true ]. + startIndex > parts size ifTrue: [ ^ false ]. + + startIndex to: parts size do: [ :i | + | part maxPrefix | + part := parts at: i. + maxPrefix := token size min: part size. + + 1 to: maxPrefix do: [ :prefixSize | + | piece | + piece := part first: prefixSize. + (token beginsWith: piece) ifTrue: [ + (self + matchToken: (token allButFirst: prefixSize) + againstParts: parts + partIndex: i + 1) ifTrue: [ ^ true ] ] ] ]. + + ^ false +] + +{ #category : 'matching' } +NECGlobalEntry >> matchesCamelCaseCompletionToken: aString caseSensitive: aBoolean [ + + | token parts normalizedParts normalizedToken | + aString ifEmpty: [ ^ true ]. + + normalizedToken := aBoolean + ifTrue: [ aString asString ] + ifFalse: [ aString asString asLowercase ]. + + parts := self camelCasePartsOf: self contents. + parts isEmpty ifTrue: [ ^ false ]. + + normalizedParts := aBoolean + ifTrue: [ parts ] + ifFalse: [ parts collect: [ :each | each asLowercase ] ]. + + token := normalizedToken. + ^ self matchToken: token againstParts: normalizedParts +] + { #category : 'accessing' } NECGlobalEntry >> rawLabel [ ^ 'global'