in scala/scala-impl/src/org/jetbrains/plugins/scala/codeInsight/intention/expression/ConvertUnderscoreToParameterIntention.scala [35:250]
override def getFamilyName: String = ScalaBundle.message("family.name.convert.underscore.section.to.parameter")
override def getText: String = getFamilyName
override def isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean =
findExpression(element, editor).isDefined
override def invoke(project: Project, editor: Editor, element: PsiElement): Unit = {
implicit val ctx: ProjectContext = project
val expr = findExpression(element, editor).get
if (expr == null || !expr.isValid) return
val buf = new mutable.StringBuilder
val underscores = ScUnderScoreSectionUtil.underscores(expr)
val parentStartOffset =
if (ApplicationManager.getApplication.isUnitTestMode) underscores.head.getTextRange.getStartOffset
else expr.getTextRange.getStartOffset
val parentEndOffset =
if (ApplicationManager.getApplication.isUnitTestMode) {
if (underscores.nonEmpty) underscores.last.getTextRange.getEndOffset
else underscores.head.getTextRange.getEndOffset
} else expr.getTextRange.getEndOffset
val underscoreToParam: mutable.HashMap[ScUnderscoreSection, ScReferenceExpression] =
new mutable.HashMap[ScUnderscoreSection, ScReferenceExpression]
val offsets: mutable.HashMap[String, Int] = new mutable.HashMap[String, Int]
val usedNames: mutable.HashSet[String] = new mutable.HashSet[String]
val macros: mutable.HashSet[String] = new mutable.HashSet[String]
var needComma = false
var needBraces = false
for (m <- Macro.EP_NAME.getExtensionList.asScala) {
macros.add(m.getName)
}
for (u <- underscores) {
if (needComma) buf.append(",")
if (underscores.size > 1) needComma = true
val names = NameSuggester.suggestNames(u,
new ScalaVariableValidator(u, false, expr.getContext, expr.getContext) {
override def validateName(name: String): String = {
var res = super.validateName(name)
var index = 1
if (usedNames.contains(res)) {
val indexStr = res.replaceAll(name, "")
if (indexStr != "") index = Integer.valueOf(indexStr)
while (usedNames.contains(name + index)) {
index = index + 1
}
} else {
return res
}
res = name + index
res
}
})
val un = names.toList match {
case head :: _ if !macros.contains(head) => head
case _ :: head :: _ => head
case _ => "value"
}
usedNames.add(un)
buf.append(un)
u.getParent match {
case typedExpr: ScTypedExpression =>
needBraces = true
buf.append(": ").append(typedExpr.`type`().get.canonicalText)
case _ =>
}
val newParam = createReferenceExpressionFromText(un)
underscoreToParam.put(u, newParam)
}
IntentionPreviewUtils.write { () =>
for (u <- underscores) {
val param = underscoreToParam.get(u)
val replaced = u.replace(param.get).asInstanceOf[ScReferenceExpression]
underscoreToParam.put(u, replaced)
offsets.put(param.get.refName, replaced.getTextRange.getStartOffset)
}
}
if (underscores.size > 1 || needBraces) buf.insert(0, "(").append(")")
val arrow = ScalaPsiUtil.functionArrow
buf.append(s" $arrow ")
val diff = buf.length
val newExpressionText = usedNames.foldLeft(expr.getText) { (text, un) =>
val regex = raw"\(\s*${un}\s*\:\s*\S+\s*\)" // looks for `(un: ...)`
Pattern.compile(regex).matcher(text).replaceFirst { mr: MatchResult =>
val start = mr.start()
if (start > 0 && text(start - 1).isLetterOrDigit) s"($un)" else un
}
}
buf.append(newExpressionText)
val newExpr = createExpressionFromText(buf.toString(), expr)
val newParentEndOffset =
if (ApplicationManager.getApplication.isUnitTestMode) {
parentEndOffset
} else {
parentEndOffset + newExpr.getText.length - expr.getText.length + 1
}
IntentionPreviewUtils.write { () =>
val document = editor.getDocument
expr.replace(newExpr)
PsiDocumentManager.getInstance(project).commitDocument(document)
if (IntentionPreviewUtils.isIntentionPreviewActive) return
val file = PsiDocumentManager.getInstance(project).getPsiFile(document)
val parent = PsiTreeUtil.findCommonParent(file.findElementAt(parentStartOffset), file.findElementAt(newParentEndOffset))
val builder: TemplateBuilderImpl = TemplateBuilderFactory.getInstance().
createTemplateBuilder(parent).asInstanceOf[TemplateBuilderImpl]
val params = new mutable.HashMap[Int, String]()
val depends = new mutable.HashMap[Int, String]()
var index: Int = 1
parent match {
case f: ScFunctionExpr =>
for (parameter <- f.parameters) {
val lookupExpr = new MyLookupExpression(parameter.name, null, parameter, f, false, null)
builder.replaceElement(parameter.nameId, parameter.name, lookupExpr, true)
val dependantParam = file.findElementAt(offsets(parameter.name) + diff)
builder.replaceElement(dependantParam, parameter.name + "_1", parameter.name, false)
params.put(index, parameter.name)
depends.put(index, parameter.name + "_1")
index = index + 1
}
case _ =>
}
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(parent)
editor.getCaretModel.moveToOffset(parent.getTextRange.getStartOffset)
val template = builder.buildInlineTemplate()
val myHighlighters = new ArrayBuffer[RangeHighlighter]
val rangesToHighlight: mutable.HashMap[TextRange, TextAttributesKey] = new mutable.HashMap
TemplateManager.getInstance(project).startTemplate(editor, template, new TemplateEditingAdapter {
override def waitingForInput(template: Template): Unit = {
markCurrentVariables(1)
}
override def currentVariableChanged(templateState: TemplateState, template: Template,
oldIndex: Int, newIndex: Int): Unit = {
if (oldIndex >= 0) clearHighlighters()
if (newIndex > 0) markCurrentVariables(newIndex + 1)
}
override def templateCancelled(template: Template): Unit = {
clearHighlighters()
}
override def templateFinished(template: Template, brokenOff: Boolean): Unit = {
clearHighlighters()
}
private def addHighlights(ranges: mutable.HashMap[TextRange, TextAttributesKey], editor: Editor,
highlighters: ArrayBuffer[RangeHighlighter], highlightManager: HighlightManager): Unit = {
for ((range, attributes) <- ranges) {
highlightManager.addOccurrenceHighlight(
editor, range.getStartOffset, range.getEndOffset,
attributes, 0, highlighters.asJava)
}
for (highlighter <- highlighters) {
highlighter.setGreedyToLeft(true)
highlighter.setGreedyToRight(true)
}
}
private def markCurrentVariables(index: Int): Unit = {
val templateState: TemplateState = TemplateManagerImpl.getTemplateState(editor)
var i: Int = 0
while (i < templateState.getSegmentsCount) {
val segmentOffset: TextRange = templateState.getSegmentRange(i)
val name: String = template.getSegmentName(i)
var attributes: TextAttributesKey = null
if (name == params(index)) {
attributes = EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES
}
else if (name == depends(index)) {
attributes = EditorColors.SEARCH_RESULT_ATTRIBUTES
}
if (attributes != null) rangesToHighlight.put(segmentOffset, attributes)
i += 1
}
addHighlights(rangesToHighlight, editor, myHighlighters, HighlightManager.getInstance(project))
}
private def clearHighlighters(): Unit = {
val highlightManager = HighlightManager.getInstance(project)
myHighlighters.foreach { a => highlightManager.removeSegmentHighlighter(editor, a) }
rangesToHighlight.clear()
myHighlighters.clear()
}
})
PsiDocumentManager.getInstance(project).commitDocument(document)
}
}