forked from scala-ide/scala-ide
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ScalaQuickFixProcessor.scala
158 lines (141 loc) · 7.08 KB
/
ScalaQuickFixProcessor.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package scala.tools.eclipse.quickfix
// Eclipse
import org.eclipse.core.runtime.CoreException
import org.eclipse.core.runtime.NullProgressMonitor
import org.eclipse.ui.IEditorPart
import org.eclipse.ui.texteditor.ITextEditor
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
import org.eclipse.jdt.ui.JavaUI
import org.eclipse.jdt.core.ICompilationUnit
import org.eclipse.jdt.core.search.IJavaSearchConstants
import org.eclipse.jdt.internal.codeassist.ISearchRequestor
import org.eclipse.jdt.internal.compiler.env.{ AccessRestriction }
import org.eclipse.jdt.internal.core.{ DefaultWorkingCopyOwner, JavaProject }
import org.eclipse.jdt.internal.ui.text.correction.{ SimilarElement, SimilarElementsRequestor }
import org.eclipse.jdt.ui.text.java._
import org.eclipse.jdt.core.search.{ TypeNameMatch, SearchEngine }
import org.eclipse.jdt.core.IJavaElement
import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector
import org.eclipse.jface.text.source.Annotation
import org.eclipse.jface.text.Position
import org.eclipse.jface.text.IDocument
// Scala IDE
import scala.tools.eclipse.javaelements.ScalaSourceFile
import scala.tools.eclipse.util.FileUtils
import scala.tools.eclipse.util.EditorUtils.getAnnotationsAtOffset
import scala.tools.eclipse.semantichighlighting.implicits.ImplicitHighlightingPresenter
import scala.tools.eclipse.logging.HasLogger
// Scala
import scala.util.matching.Regex
import collection.JavaConversions._
class ScalaQuickFixProcessor extends IQuickFixProcessor with HasLogger {
private val typeNotFoundError = new Regex("not found: type (.*)")
private val valueNotFoundError = new Regex("not found: value (.*)")
private val xxxxxNotFoundError = new Regex("not found: (.*)")
// regex for extracting expected and required type on type mismatch
private val typeMismatchError = new Regex("type mismatch;\\s*found\\s*: (\\S*)\\s*required: (.*)")
/**
* Checks if the processor has any corrections.
*
* Currently this always returns true. At some point it may be worthwhile
* to expend some effort on implementing this properly to make the plug-in
* slightly more responsive.
*/
def hasCorrections(unit : ICompilationUnit, problemId : Int) : Boolean = true
/**
* Collects corrections or code manipulations for the given context.
*
* @param context Defines current compilation unit, position and a shared AST
* @param locations Problems are the current location.
* @return the corrections applicable at the location or <code>null</code> if no proposals
* can be offered
* @throws CoreException CoreException can be thrown if the operation fails
*/
def getCorrections(context : IInvocationContext, locations : Array[IProblemLocation]) : Array[IJavaCompletionProposal] =
context.getCompilationUnit match {
case ssf : ScalaSourceFile => {
val editor = JavaUI.openInEditor(context.getCompilationUnit)
var corrections : List[IJavaCompletionProposal] = Nil
for (location <- locations)
for ((ann, pos) <- getAnnotationsAtOffset(editor, location.getOffset)) {
val importFix = suggestImportFix(context.getCompilationUnit(), ann.getText)
// compute all possible type mismatch quick fixes
val document = (editor.asInstanceOf[ITextEditor]).getDocumentProvider().getDocument(editor.getEditorInput())
val typeMismatchFix = suggestTypeMismatchFix(document, ann.getText, pos)
// concatenate lists of found quick fixes
corrections = corrections ++ importFix ++ typeMismatchFix
}
corrections match {
case Nil => null
case l => l.distinct.toArray
}
}
case _ => null
}
// XXX is this code duplication? -- check scala.tools.eclipse.util.EditorUtils.getAnnotationsAtOffset
private def getAnnotationsAtOffsetXXX(part: IEditorPart, offset: Int): List[Annotation] = {
import ScalaQuickFixProcessor._
var ret = List[Annotation]()
val model = JavaUI.getDocumentProvider().getAnnotationModel(part.getEditorInput())
val iter = model.getAnnotationIterator
while (iter.hasNext()) {
val ann: Annotation = iter.next().asInstanceOf[Annotation]
val pos = model.getPosition(ann)
if (isInside(offset, pos.offset, pos.offset + pos.length))
ret = ann :: ret
}
return ret
}
private
def suggestImportFix(compilationUnit : ICompilationUnit, problemMessage : String) : List[IJavaCompletionProposal] = {
/**
* Import a type could solve several error message :
*
* * "not found : type Xxxx"
* * "not found : value Xxxx" in case of java static constant/method like Xxxx.ZZZZ or Xxxx.zzz()
* * "not found : Xxxx" in case of new Xxxx.eee (IMO (davidB) a better suggestion is to insert (), to have new Xxxx().eeee )
*/
def suggestImportType(missingType : String) : List[IJavaCompletionProposal] = {
val resultCollector = new java.util.ArrayList[TypeNameMatch]
val scope = SearchEngine.createJavaSearchScope(Array[IJavaElement](compilationUnit.getJavaProject))
val typesToSearch = Array(missingType.toArray)
new SearchEngine().searchAllTypeNames(null, typesToSearch, scope, new TypeNameMatchCollector(resultCollector), IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, new NullProgressMonitor)
resultCollector map { typeFound =>
new ImportCompletionProposal(typeFound.getFullyQualifiedName)
} toList
}
return problemMessage match {
case typeNotFoundError(missingType) => suggestImportType(missingType)
case valueNotFoundError(missingValue) => suggestImportType(missingValue)
case xxxxxNotFoundError(missing) => suggestImportType(missing)
case _ => Nil
}
}
private
def suggestTypeMismatchFix(document : IDocument, problemMessage : String, location: Position) : List[IJavaCompletionProposal] = {
// get the annotation string
val annotationString = document.get(location.getOffset, location.getLength)
// match problem message
return problemMessage match {
// extract found and required type
case typeMismatchError(foundType, requiredType) =>
// utilize type mismatch computer to find quick fixes
val replacementStringList = TypeMismatchQuickFixProcessor(foundType, requiredType, annotationString)
// map replacements strings into expanding proposals
replacementStringList map {
replacementString =>
// make markers message in form: "... =>replacement"
val markersMessage = annotationString + " " + ImplicitHighlightingPresenter.DisplayStringSeparator + replacementString
// construct a proposal with the appropriate location
new ExpandingProposalBase(markersMessage, "Transform your code expression: ", location)
}
// no match found for the problem message
case _ => Nil
}
}
}
object ScalaQuickFixProcessor {
private def isInside(offset: Int, start: Int,end: Int): Boolean = {
return offset == start || offset == end || (offset > start && offset < end); // make sure to handle 0-length ranges
}
}