Skip to content

Commit c2a4733

Browse files
committed
completion: refine ns form variants
1 parent fd27220 commit c2a4733

File tree

5 files changed

+43
-24
lines changed

5 files changed

+43
-24
lines changed

src/lang/clojure-editor-psi.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class ClojureCompletionContributor : CompletionContributor() {
221221
}
222222
if (thisForm !is CKeyword && ref != null && bindingsVec == null) {
223223
val service = ClojureDefinitionService.getInstance(project)
224-
ref.processDeclarations(service, null, ResolveState.initial(), object : BaseScopeProcessor() {
224+
val stop = !ref.processDeclarations(service, null, ResolveState.initial(), object : BaseScopeProcessor() {
225225
override fun execute(it: PsiElement, state: ResolveState): Boolean {
226226
val name = state.get(RENAMED_KEY) ?:
227227
when (it) {
@@ -256,6 +256,7 @@ class ClojureCompletionContributor : CompletionContributor() {
256256
return true
257257
}
258258
})
259+
if (stop && !showAll) return
259260
if (showAll) {
260261
FileBasedIndex.getInstance().run {
261262
val scope = ClojureDefinitionService.getClojureSearchScope(project)

src/lang/clojure-psi-fileimpl.kt

+30-19
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package org.intellij.clojure.psi.impl
1919

2020
import com.intellij.extapi.psi.PsiFileBase
2121
import com.intellij.lang.Language
22-
import com.intellij.openapi.util.Key
2322
import com.intellij.openapi.util.TextRange
2423
import com.intellij.openapi.util.text.StringUtil
2524
import com.intellij.openapi.vfs.VirtualFileWithId
@@ -32,6 +31,7 @@ import com.intellij.util.SmartList
3231
import com.intellij.util.containers.JBIterable
3332
import com.intellij.util.containers.JBTreeTraverser
3433
import com.intellij.util.containers.TreeTraversal
34+
import com.intellij.util.indexing.FileBasedIndex
3535
import org.intellij.clojure.ClojureConstants
3636
import org.intellij.clojure.editor.arguments
3737
import org.intellij.clojure.lang.ClojureFileType
@@ -44,7 +44,7 @@ import org.intellij.clojure.util.*
4444
import java.lang.ref.SoftReference
4545
import java.util.*
4646

47-
private val EXPLICIT_RESOLVE_KEY: Key<SymKey> = Key.create("EXPLICIT_RESOLVE_KEY")
47+
private data class ResolveTo(val key: SymKey)
4848
private val ALL: Set<String> = setOf("* all *")
4949
val PRIVATE_META = "#private"
5050
val TYPE_META = "#typeHint"
@@ -196,8 +196,8 @@ class CFileImpl(viewProvider: FileViewProvider, language: Language) :
196196
val namespace = namespace
197197
val forceAlias = state.get(ALIAS_KEY)
198198
var langKindNSVisited = false
199+
val thisImport = this.state.imports.find { it.range.contains(placeOffset) }
199200
val imports = importsAtOffset(placeOffset, dialect)
200-
val insideImport = imports.find { it.range.contains(placeOffset) } != null
201201
for (import in imports.flatMap { it.imports }) {
202202
if (refText == null || isQualifier) {
203203
if (!import.isPlatform && import.aliasSym != null) {
@@ -222,7 +222,7 @@ class CFileImpl(viewProvider: FileViewProvider, language: Language) :
222222
langKindNSVisited = langKindNSVisited || dialect.coreNs == import.namespace
223223
val refersByDefault = import.nsType == "refer" || import.nsType == "refer-clojure" || import.nsType == "use"
224224
if (!processNamespace(import.namespace, dialect, state,
225-
if (insideImport) processor
225+
if (thisImport != null && thisImport.imports.contains(import)) processor
226226
else object : PsiScopeProcessor by processor {
227227
override fun execute(element: PsiElement, state: ResolveState): Boolean {
228228
val name = element.asCTarget?.key?.name ?: element.asDef?.def?.name ?: return true
@@ -269,11 +269,10 @@ class CFileImpl(viewProvider: FileViewProvider, language: Language) :
269269
.associateBy({ it.namespace }, { it.alias })
270270
}
271271

272-
273272
internal fun processPrecomputedDeclarations(refText: String?, place: CSymbol, dialect: Dialect,
274273
service: ClojureDefinitionService, state: ResolveState,
275274
processor: PsiScopeProcessor): Boolean {
276-
val key = EXPLICIT_RESOLVE_KEY.get(place) ?: return true
275+
val key = ((place as? CComposite)?.data as? ResolveTo)?.key ?: return true
277276
val target = when (key.type) {
278277
"java package" ->
279278
if (dialect == Dialect.CLJS) return processor.skipResolve()
@@ -289,12 +288,20 @@ class CFileImpl(viewProvider: FileViewProvider, language: Language) :
289288
if (target != null) {
290289
if (key.type == "def") {
291290
if (!processNamespace(key.namespace, dialect, state, processor, this)) return false
292-
return processSpecialForms(dialect, refText, place, service, state, processor)
291+
if (key.namespace == dialect.coreNs) {
292+
if (!processSpecialForms(dialect, refText, place, service, state, processor)) return false
293+
}
294+
}
295+
else if (refText != null) {
296+
if (!processor.execute(target, state)) return false
297+
}
298+
else if (key.type == "ns") {
299+
FileBasedIndex.getInstance().processAllKeys(NS_INDEX, { ns ->
300+
processor.execute(service.getNamespace(ns), state)
301+
}, project)
293302
}
294-
if (refText != null) return processor.execute(target, state)
295-
return true
296303
}
297-
return true
304+
return false
298305
}
299306
}
300307

@@ -504,7 +511,7 @@ private class RoleHelper {
504511
setData(e, ns ?: "")
505512
}
506513

507-
fun processNSElement(e: CListBase) {
514+
private fun processNSElement(e: CListBase) {
508515
e.cljTraverser().filter(CKeywordBase::class.java).onEach { processKeyword(it) }
509516
nsReader.processElement(e)
510517
}
@@ -585,7 +592,7 @@ private class NSReader(val helper: RoleHelper) {
585592
val name = o.name
586593
val qualifiedName = name.withPackage(prefix)
587594
classes.add(qualifiedName)
588-
o.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(name, prefix, "java class"))
595+
setResolveTo(o, SymKey(name, prefix, "java class"))
589596
}
590597
for (item in iterator) {
591598
when (item) {
@@ -599,7 +606,7 @@ private class NSReader(val helper: RoleHelper) {
599606
addClass(it as? CSymbol ?: return@forEach, packageName)
600607
if (anyClass == "") anyClass = it.name
601608
}
602-
packageSym.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(anyClass, packageName, "java package"))
609+
setResolveTo(packageSym, SymKey(anyClass, packageName, "java package"))
603610
}
604611
}
605612
}
@@ -613,7 +620,7 @@ private class NSReader(val helper: RoleHelper) {
613620
val nsSym = iterator.safeNext() as? CSymbol
614621
val namespace = nsSym?.name ?: ""
615622
setData(aliasSym, Role.NAME)
616-
nsSym?.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(namespace, "", "ns"))
623+
setResolveTo(nsSym, SymKey(namespace, "", "ns"))
617624
return listOf(Import("alias", namespace, aliasSym.name, aliasSym))
618625
}
619626

@@ -639,17 +646,17 @@ private class NSReader(val helper: RoleHelper) {
639646
}
640647
val namespace = if (forcedNamespace) nsPrefix else (nsSym?.name?.withPackage(nsPrefix) ?: "")
641648
val alias = aliasSym?.name ?: ""
642-
nsSym?.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(namespace, "", "ns"))
643-
aliasSym?.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(alias, namespace, "alias"))
649+
setResolveTo(nsSym, SymKey(namespace, "", "ns"))
650+
setResolveTo(aliasSym, SymKey(alias, namespace, "alias"))
644651
fun CPForm?.toNames() = traverser.withRoot(this).filter(CSymbol::class.java)
645-
.transform { sym -> sym.name.also { sym.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(it, namespace, "def")) } }.toSet()
652+
.transform { sym -> sym.name.also { setResolveTo(sym, SymKey(it, namespace, "def")) } }.toSet()
646653

647654
val import = Import(nsType, namespace, alias, aliasSym,
648655
(refer as? CPForm)?.toNames() ?: (refer as? CKeyword)?.let { if (it.name == "all") ALL else null } ?: emptySet(),
649656
only.toNames(), exclude.toNames(),
650657
rename?.split(2, true)?.reduce(HashMap()) { map, o ->
651658
if (o.size == 2) map.put(o[0].name,
652-
o[1].also { it.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(it.name, namespace, "alias")) }); map
659+
o[1].also { setResolveTo(it, SymKey(it.name, namespace, "alias")) }); map
653660
} ?: emptyMap())
654661
return import
655662
}
@@ -662,7 +669,7 @@ private class NSReader(val helper: RoleHelper) {
662669
when (item) {
663670
is CSymbol -> item.name.withPackage(nsPrefix).let { ns ->
664671
result.add(Import(nsType, ns, "", null))
665-
item.putUserData(EXPLICIT_RESOLVE_KEY, SymKey(ns, "", "ns"))
672+
setResolveTo(item, SymKey(ns, "", "ns"))
666673
}
667674
is CVec -> result.addAll(readNSElement_refer(nsPrefix, traverser.iterate(item), traverser, nsType).asListOrEmpty())
668675
}
@@ -712,6 +719,10 @@ private fun setData(o: PsiElement?, data: Any?) {
712719
if (o is CComposite) o.dataImpl = data
713720
}
714721

722+
private fun setResolveTo(o: CSymbol?, key: SymKey) {
723+
setData(o, ResolveTo(key))
724+
}
725+
715726
fun PsiElement?.rcTraverser(rcKey: String) = cljTraverser()
716727
.forceDisregard { e ->
717728
val r = e.fastRole

src/lang/clojure-psi-symbols.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ internal class YTarget(project: Project,
235235
get() {
236236
val userData = map[key]?.getUserData(SOURCE_KEY)
237237
val retrieved = (userData as? PsiAnchor)?.retrieve()
238-
if (retrieved is PsiElement) return retrieved
238+
if (retrieved is PsiElement && retrieved.isValid) return retrieved
239239
val modificationCount = PsiModificationTracker.SERVICE.getInstance(project).modificationCount
240240
if (userData is Long && userData == modificationCount) return null
241241
if (DumbService.getInstance(project).isDumb) return null

testData/highlighting/ClojureScript.txt

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/cljs/analyzer.cljc 0 errors, 16 warnings, 23 dynamic
1+
/cljs/analyzer.cljc 0 errors, 17 warnings, 23 dynamic
2+
604: unable to resolve 'ensure'
23
1981: unable to resolve 'cljs.tagged_literals'
34
2002: unable to resolve 'JSValue'
45
98158: unable to resolve 'JSValue'
@@ -26,7 +27,8 @@
2627
58620: unable to resolve 'read-str'
2728
70115: unable to resolve 'getResources'
2829
86775: unable to resolve 'poll'
29-
/cljs/compiler.cljc 0 errors, 4 warnings, 24 dynamic
30+
/cljs/compiler.cljc 0 errors, 5 warnings, 24 dynamic
31+
604: unable to resolve 'ensure'
3032
42107: unable to resolve 'write-str'
3133
42154: unable to resolve 'read-str'
3234
43267: unable to resolve '*alias-map*'
@@ -195,4 +197,4 @@
195197
/clojure/string.cljs 0 errors, 0 warnings, 48 dynamic
196198
/clojure/walk.cljs 0 errors, 0 warnings, 0 dynamic
197199
/clojure/zip.cljs 0 errors, 0 warnings, 0 dynamic
198-
Total: 144 warnings in 53 files (1.19M chars)
200+
Total: 146 warnings in 53 files (1.19M chars)

tests/lang/completion-tests.kt

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ class ClojureCompletionTest : LightPlatformCodeInsightFixtureTestCase() {
6060
fun testFqn2a() = "str/blank?".let { doTest("$STR_ALIAS (bla<caret>)", it, "$STR_ALIAS ($it)", 2) }
6161
fun testFqn3a() = "str/blank?".let { doTest("$STR_ALIAS (clostribla<caret>)", it, "$STR_ALIAS ($it)", 2) }
6262

63+
fun testInsideImport1() = doTest("(require [<caret> :refer [blank?]])", "clojure.string")
64+
fun testInsideImport2() = doNegTest("(require [<caret> :refer [blank?]])", "def")
65+
fun testInsideImport3() = doTest("(require [clojure.string :refer [<caret>]])", "blank?")
66+
fun testInsideImport4() = doNegTest("(require [clojure.string :refer [<caret>]])", "def")
67+
6368

6469
private fun doTest(text: String, select: String,
6570
expected: String = text.replace("<caret>", select),

0 commit comments

Comments
 (0)