Refactoring
This commit is contained in:
+36
-10
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -30,9 +32,7 @@ import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.*
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.KotlinKeywordInsertHandler
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.UseSiteAnnotationTargetInsertHandler
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLookupElement
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
@@ -72,6 +72,20 @@ object KeywordCompletion {
|
||||
CONSTRUCTOR_KEYWORD to "class C { constructor(caret)"
|
||||
)
|
||||
|
||||
private val NO_SPACE_AFTER = listOf(THIS_KEYWORD,
|
||||
SUPER_KEYWORD,
|
||||
NULL_KEYWORD,
|
||||
TRUE_KEYWORD,
|
||||
FALSE_KEYWORD,
|
||||
BREAK_KEYWORD,
|
||||
CONTINUE_KEYWORD,
|
||||
ELSE_KEYWORD,
|
||||
WHEN_KEYWORD,
|
||||
FILE_KEYWORD,
|
||||
DYNAMIC_KEYWORD,
|
||||
GET_KEYWORD,
|
||||
SET_KEYWORD).map { it.value} + "companion object"
|
||||
|
||||
fun complete(position: PsiElement, prefix: String, isJvmModule: Boolean, consumer: (LookupElement) -> Unit) {
|
||||
if (!GENERAL_FILTER.isAcceptable(position, position)) return
|
||||
|
||||
@@ -109,12 +123,14 @@ object KeywordCompletion {
|
||||
|
||||
val isUseSiteAnnotationTarget = position.prevLeaf()?.node?.elementType == KtTokens.AT
|
||||
|
||||
val insertHandler = if (isUseSiteAnnotationTarget)
|
||||
UseSiteAnnotationTargetInsertHandler
|
||||
else if (keywordToken !in FUNCTION_KEYWORDS)
|
||||
KotlinKeywordInsertHandler
|
||||
else
|
||||
KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = false)
|
||||
val insertHandler = when {
|
||||
isUseSiteAnnotationTarget -> UseSiteAnnotationTargetInsertHandler
|
||||
|
||||
keyword in NO_SPACE_AFTER -> null
|
||||
|
||||
else -> SpaceAfterInsertHandler
|
||||
}
|
||||
|
||||
element = element.withInsertHandler(insertHandler)
|
||||
|
||||
if (isUseSiteAnnotationTarget) {
|
||||
@@ -126,7 +142,17 @@ object KeywordCompletion {
|
||||
}
|
||||
}
|
||||
|
||||
private val FUNCTION_KEYWORDS = listOf(CONSTRUCTOR_KEYWORD)
|
||||
private object UseSiteAnnotationTargetInsertHandler : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
WithTailInsertHandler(":", spaceBefore = false, spaceAfter = false).postHandleInsert(context, item)
|
||||
}
|
||||
}
|
||||
|
||||
private object SpaceAfterInsertHandler : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
WithTailInsertHandler.SPACE.postHandleInsert(context, item)
|
||||
}
|
||||
}
|
||||
|
||||
private val GENERAL_FILTER = NotFilter(OrFilter(
|
||||
CommentFilter(),
|
||||
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion.handlers
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import org.jetbrains.kotlin.idea.completion.KeywordLookupObject
|
||||
import org.jetbrains.kotlin.idea.core.moveCaret
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
|
||||
object KotlinKeywordInsertHandler : InsertHandler<LookupElement> {
|
||||
private val NO_SPACE_AFTER = listOf(THIS_KEYWORD,
|
||||
SUPER_KEYWORD,
|
||||
FOR_KEYWORD,
|
||||
NULL_KEYWORD,
|
||||
TRUE_KEYWORD,
|
||||
FALSE_KEYWORD,
|
||||
BREAK_KEYWORD,
|
||||
CONTINUE_KEYWORD,
|
||||
IF_KEYWORD,
|
||||
ELSE_KEYWORD,
|
||||
WHILE_KEYWORD,
|
||||
DO_KEYWORD,
|
||||
TRY_KEYWORD,
|
||||
WHEN_KEYWORD,
|
||||
FILE_KEYWORD,
|
||||
CATCH_KEYWORD,
|
||||
FINALLY_KEYWORD,
|
||||
DYNAMIC_KEYWORD,
|
||||
GET_KEYWORD,
|
||||
SET_KEYWORD).map { it.value } + "companion object"
|
||||
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val keyword = item.lookupString
|
||||
if (keyword !in NO_SPACE_AFTER) {
|
||||
WithTailInsertHandler.SPACE.postHandleInsert(context, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createKeywordConstructLookupElement(
|
||||
project: Project,
|
||||
keyword: String,
|
||||
fileTextToReformat: String,
|
||||
trimSpacesAroundCaret: Boolean = false,
|
||||
showConstructInLookup: Boolean = true
|
||||
): LookupElement {
|
||||
val file = KtPsiFactory(project).createFile(fileTextToReformat)
|
||||
CodeStyleManager.getInstance(project).reformat(file)
|
||||
val newFileText = file.text
|
||||
|
||||
val keywordOffset = newFileText.indexOf(keyword)
|
||||
assert(keywordOffset >= 0)
|
||||
val keywordEndOffset = keywordOffset + keyword.length
|
||||
|
||||
val caretPlaceHolder = "caret"
|
||||
|
||||
val caretOffset = newFileText.indexOf(caretPlaceHolder)
|
||||
assert(caretOffset >= 0)
|
||||
assert(caretOffset >= keywordEndOffset)
|
||||
|
||||
var tailBeforeCaret = newFileText.substring(keywordEndOffset, caretOffset)
|
||||
var tailAfterCaret = newFileText.substring(caretOffset + caretPlaceHolder.length)
|
||||
|
||||
if (trimSpacesAroundCaret) {
|
||||
tailBeforeCaret = tailBeforeCaret.trimEnd()
|
||||
tailAfterCaret = tailAfterCaret.trimStart()
|
||||
}
|
||||
|
||||
val indent = detectIndent(newFileText, keywordOffset)
|
||||
if (indent != null) {
|
||||
tailBeforeCaret = tailBeforeCaret.unindent(indent)
|
||||
tailAfterCaret = tailAfterCaret.unindent(indent)
|
||||
}
|
||||
|
||||
var lookupElementBuilder = LookupElementBuilder.create(KeywordLookupObject(), keyword)
|
||||
.bold()
|
||||
.withInsertHandler { insertionContext, lookupElement ->
|
||||
if (insertionContext.completionChar == Lookup.NORMAL_SELECT_CHAR || insertionContext.completionChar == Lookup.REPLACE_SELECT_CHAR) {
|
||||
val offset = insertionContext.tailOffset
|
||||
val newIndent = detectIndent(insertionContext.document.charsSequence, offset - keyword.length)
|
||||
var beforeCaret = tailBeforeCaret
|
||||
var afterCaret = tailAfterCaret
|
||||
if (newIndent != null) {
|
||||
beforeCaret = beforeCaret.indentLinesAfterFirst(newIndent)
|
||||
afterCaret = afterCaret.indentLinesAfterFirst(newIndent)
|
||||
}
|
||||
insertionContext.document.insertString(offset, beforeCaret + afterCaret)
|
||||
insertionContext.editor.moveCaret(offset + beforeCaret.length)
|
||||
}
|
||||
}
|
||||
if (showConstructInLookup) {
|
||||
lookupElementBuilder = lookupElementBuilder.withTailText(tailBeforeCaret + tailAfterCaret)
|
||||
}
|
||||
return lookupElementBuilder
|
||||
}
|
||||
|
||||
private fun detectIndent(text: CharSequence, offset: Int): String? {
|
||||
val reversedIndent = buildString {
|
||||
var index = offset - 1
|
||||
Loop@
|
||||
while (index >= 0) {
|
||||
val c = text[index]
|
||||
when (c) {
|
||||
' ', '\t' -> append(c)
|
||||
'\r', '\n' -> break@Loop
|
||||
else -> return null
|
||||
}
|
||||
index--
|
||||
}
|
||||
}
|
||||
return reversedIndent.reversed()
|
||||
}
|
||||
|
||||
private fun String.indentLinesAfterFirst(indent: String): String {
|
||||
val text = this
|
||||
return buildString {
|
||||
val lines = text.lines()
|
||||
for ((index, line) in lines.withIndex()) {
|
||||
if (index > 0) append(indent)
|
||||
append(line)
|
||||
if (index != lines.lastIndex) append('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.unindent(indent: String): String {
|
||||
val text = this
|
||||
return buildString {
|
||||
val lines = text.lines()
|
||||
for ((index, line) in lines.withIndex()) {
|
||||
append(line.removePrefix(indent))
|
||||
if (index != lines.lastIndex) append('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object UseSiteAnnotationTargetInsertHandler : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
WithTailInsertHandler(":", spaceBefore = false, spaceAfter = false).postHandleInsert(context, item)
|
||||
}
|
||||
}
|
||||
+105
@@ -18,10 +18,18 @@ package org.jetbrains.kotlin.idea.completion.handlers
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import org.jetbrains.kotlin.idea.completion.KeywordLookupObject
|
||||
import org.jetbrains.kotlin.idea.core.moveCaret
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
|
||||
fun surroundWithBracesIfInStringTemplate(context: InsertionContext) {
|
||||
val startOffset = context.startOffset
|
||||
@@ -61,3 +69,100 @@ fun CharSequence.skipSpacesAndLineBreaks(index: Int): Int
|
||||
fun CharSequence.isCharAt(offset: Int, c: Char) = offset < length && this[offset] == c
|
||||
|
||||
fun Document.isTextAt(offset: Int, text: String) = offset + text.length <= textLength && getText(TextRange(offset, offset + text.length)) == text
|
||||
|
||||
fun createKeywordConstructLookupElement(
|
||||
project: Project,
|
||||
keyword: String,
|
||||
fileTextToReformat: String,
|
||||
trimSpacesAroundCaret: Boolean = false,
|
||||
showConstructInLookup: Boolean = true
|
||||
): LookupElement {
|
||||
val file = KtPsiFactory(project).createFile(fileTextToReformat)
|
||||
CodeStyleManager.getInstance(project).reformat(file)
|
||||
val newFileText = file.text
|
||||
|
||||
val keywordOffset = newFileText.indexOf(keyword)
|
||||
assert(keywordOffset >= 0)
|
||||
val keywordEndOffset = keywordOffset + keyword.length
|
||||
|
||||
val caretPlaceHolder = "caret"
|
||||
|
||||
val caretOffset = newFileText.indexOf(caretPlaceHolder)
|
||||
assert(caretOffset >= 0)
|
||||
assert(caretOffset >= keywordEndOffset)
|
||||
|
||||
var tailBeforeCaret = newFileText.substring(keywordEndOffset, caretOffset)
|
||||
var tailAfterCaret = newFileText.substring(caretOffset + caretPlaceHolder.length)
|
||||
|
||||
if (trimSpacesAroundCaret) {
|
||||
tailBeforeCaret = tailBeforeCaret.trimEnd()
|
||||
tailAfterCaret = tailAfterCaret.trimStart()
|
||||
}
|
||||
|
||||
val indent = detectIndent(newFileText, keywordOffset)
|
||||
if (indent != null) {
|
||||
tailBeforeCaret = tailBeforeCaret.unindent(indent)
|
||||
tailAfterCaret = tailAfterCaret.unindent(indent)
|
||||
}
|
||||
|
||||
var lookupElementBuilder = LookupElementBuilder.create(KeywordLookupObject(), keyword)
|
||||
.bold()
|
||||
.withInsertHandler { insertionContext, lookupElement ->
|
||||
if (insertionContext.completionChar == Lookup.NORMAL_SELECT_CHAR || insertionContext.completionChar == Lookup.REPLACE_SELECT_CHAR) {
|
||||
val offset = insertionContext.tailOffset
|
||||
val newIndent = detectIndent(insertionContext.document.charsSequence, offset - keyword.length)
|
||||
var beforeCaret = tailBeforeCaret
|
||||
var afterCaret = tailAfterCaret
|
||||
if (newIndent != null) {
|
||||
beforeCaret = beforeCaret.indentLinesAfterFirst(newIndent)
|
||||
afterCaret = afterCaret.indentLinesAfterFirst(newIndent)
|
||||
}
|
||||
insertionContext.document.insertString(offset, beforeCaret + afterCaret)
|
||||
insertionContext.editor.moveCaret(offset + beforeCaret.length)
|
||||
}
|
||||
}
|
||||
if (showConstructInLookup) {
|
||||
lookupElementBuilder = lookupElementBuilder.withTailText(tailBeforeCaret + tailAfterCaret)
|
||||
}
|
||||
return lookupElementBuilder
|
||||
}
|
||||
|
||||
private fun detectIndent(text: CharSequence, offset: Int): String? {
|
||||
val reversedIndent = buildString {
|
||||
var index = offset - 1
|
||||
Loop@
|
||||
while (index >= 0) {
|
||||
val c = text[index]
|
||||
when (c) {
|
||||
' ', '\t' -> append(c)
|
||||
'\r', '\n' -> break@Loop
|
||||
else -> return null
|
||||
}
|
||||
index--
|
||||
}
|
||||
}
|
||||
return reversedIndent.reversed()
|
||||
}
|
||||
|
||||
private fun String.indentLinesAfterFirst(indent: String): String {
|
||||
val text = this
|
||||
return buildString {
|
||||
val lines = text.lines()
|
||||
for ((index, line) in lines.withIndex()) {
|
||||
if (index > 0) append(indent)
|
||||
append(line)
|
||||
if (index != lines.lastIndex) append('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.unindent(indent: String): String {
|
||||
val text = this
|
||||
return buildString {
|
||||
val lines = text.lines()
|
||||
for ((index, line) in lines.withIndex()) {
|
||||
append(line.removePrefix(indent))
|
||||
if (index != lines.lastIndex) append('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user