13
13
package scala .tools .nsc .interpreter
14
14
package jline
15
15
16
- import java .util .{List => JList }
17
- import org .jline .reader .{Candidate , Completer , CompletingParsedLine , EOFError , EndOfFileException , History , LineReader , ParsedLine , Parser , SyntaxError , UserInterruptException }
16
+ import java .util .{Collections , List => JList }
17
+ import org .jline .reader .{Candidate , Completer , CompletingParsedLine , EOFError , EndOfFileException , History , LineReader , ParsedLine , Parser , Reference , SyntaxError , UserInterruptException }
18
18
import org .jline .reader .impl .{CompletionMatcherImpl , DefaultParser , LineReaderImpl }
19
19
import org .jline .terminal .Terminal
20
20
21
21
import shell .{Accumulator , ShellConfig }
22
22
import Parser .ParseContext
23
+ import org .jline .console .{CmdDesc , CmdLine }
24
+ import org .jline .keymap .KeyMap
25
+ import org .jline .utils .AttributedString
26
+ import org .jline .widget .TailTipWidgets .TipType
27
+ import org .jline .widget .{TailTipWidgets }
23
28
24
29
import java .{lang , util }
30
+ import scala .reflect .internal .Chars
25
31
26
32
/** A Reader that delegates to JLine3.
27
33
*/
@@ -95,21 +101,67 @@ object Reader {
95
101
.option(Option .DISABLE_EVENT_EXPANSION , true ) // Otherwise `scala> println(raw"\n".toList)` gives `List(n)` !!
96
102
.option(Option .COMPLETE_MATCHER_CAMELCASE , true )
97
103
}
98
- builder.completionMatcher( new CompletionMatcherImpl {
104
+ object customCompletionMatcher extends CompletionMatcherImpl {
99
105
override def compile (options : util.Map [LineReader .Option , lang.Boolean ], prefix : Boolean , line : CompletingParsedLine , caseInsensitive : Boolean , errors : Int , originalGroupName : String ): Unit = {
100
106
super .compile(options, prefix, line, caseInsensitive, errors, originalGroupName)
101
107
// TODO Use Option.COMPLETION_MATCHER_TYPO(false) in once https://github.com/jline/jline3/pull/646
102
108
matchers.remove(matchers.size() - 2 )
103
- // TODO add SNAKE_CASE completion matcher.
104
109
}
105
- })
110
+
111
+ override def matches (candidates : JList [Candidate ]): JList [Candidate ] = {
112
+ val matching = super .matches(candidates)
113
+ matching
114
+ }
115
+ }
116
+
117
+ builder.completionMatcher(customCompletionMatcher)
106
118
107
119
val reader = builder.build()
120
+
121
+ val desc : java.util.function.Function [CmdLine , CmdDesc ] = (cmdLine) => new CmdDesc (util.Arrays .asList(new AttributedString (" demo" )), Collections .emptyList(), Collections .emptyMap())
122
+ new TailTipWidgets (reader, desc, 1 , TipType .COMPLETER )
123
+ val keyMap = reader.getKeyMaps.get(" main" )
124
+
125
+ object ScalaShowType {
126
+ val Name = " scala-show-type"
127
+ private var lastInvokeLocation : Option [(String , Int )] = None
128
+ def apply (): Boolean = {
129
+ val nextInvokeLocation = Some ((reader.getBuffer.toString, reader.getBuffer.cursor()))
130
+ val cursor = reader.getBuffer.cursor()
131
+ val text = reader.getBuffer.toString
132
+ val result = completer.complete(text, cursor, filter = true )
133
+ if (lastInvokeLocation == nextInvokeLocation) {
134
+ showTree(result)
135
+ lastInvokeLocation = None
136
+ } else {
137
+ showType(result)
138
+ lastInvokeLocation = nextInvokeLocation
139
+ }
140
+ true
141
+ }
142
+ def showType (result : shell.CompletionResult ): Unit = {
143
+ reader.getTerminal.writer.println()
144
+ reader.getTerminal.writer.println(result.typeAtCursor)
145
+ reader.callWidget(LineReader .REDRAW_LINE )
146
+ reader.callWidget(LineReader .REDISPLAY )
147
+ reader.getTerminal.flush()
148
+ }
149
+ def showTree (result : shell.CompletionResult ): Unit = {
150
+ reader.getTerminal.writer.println()
151
+ reader.getTerminal.writer.println(Naming .unmangle(result.typedTree))
152
+ reader.callWidget(LineReader .REDRAW_LINE )
153
+ reader.callWidget(LineReader .REDISPLAY )
154
+ reader.getTerminal.flush()
155
+ }
156
+ }
157
+ reader.getWidgets().put(ScalaShowType .Name , () => ScalaShowType ())
158
+
108
159
locally {
109
160
import LineReader ._
110
161
// VIINS, VICMD, EMACS
111
162
val keymap = if (config.viMode) VIINS else EMACS
112
163
reader.getKeyMaps.put(MAIN , reader.getKeyMaps.get(keymap));
164
+ keyMap.bind(new Reference (ScalaShowType .Name ), KeyMap .ctrl('T' ))
113
165
}
114
166
def secure (p : java.nio.file.Path ): Unit = {
115
167
try scala.reflect.internal.util.OwnerOnlyChmod .chmodFileOrCreateEmpty(p)
@@ -177,6 +229,12 @@ object Reader {
177
229
val (wordCursor, wordIndex) = current match {
178
230
case Some (t) if t.isIdentifier =>
179
231
(cursor - t.start, tokens.indexOf(t))
232
+ case Some (t) =>
233
+ val isIdentifierStartKeyword = (t.start until t.end).forall(i => Chars .isIdentifierPart(line.charAt(i)))
234
+ if (isIdentifierStartKeyword)
235
+ (cursor - t.start, tokens.indexOf(t))
236
+ else
237
+ (0 , - 1 )
180
238
case _ =>
181
239
(0 , - 1 )
182
240
}
@@ -233,15 +291,16 @@ object Reader {
233
291
* It delegates both interfaces to an underlying `Completion`.
234
292
*/
235
293
class Completion (delegate : shell.Completion ) extends shell.Completion with Completer {
294
+ var lastPrefix : String = " "
236
295
require(delegate != null )
237
296
// REPL Completion
238
297
def complete (buffer : String , cursor : Int , filter : Boolean ): shell.CompletionResult = delegate.complete(buffer, cursor, filter)
239
298
240
299
// JLine Completer
241
300
def complete (lineReader : LineReader , parsedLine : ParsedLine , newCandidates : JList [Candidate ]): Unit = {
242
301
def candidateForResult (cc : CompletionCandidate ): Candidate = {
243
- val value = cc.defString
244
- val displayed = cc.defString + (cc.arity match {
302
+ val value = cc.name
303
+ val displayed = cc.name + (cc.arity match {
245
304
case CompletionCandidate .Nullary => " "
246
305
case CompletionCandidate .Nilary => " ()"
247
306
case _ => " ("
@@ -257,23 +316,19 @@ class Completion(delegate: shell.Completion) extends shell.Completion with Compl
257
316
new Candidate (value, displayed, group, descr, suffix, key, complete)
258
317
}
259
318
val result = complete(parsedLine.line, parsedLine.cursor, filter = false )
260
- result.candidates.map(_.defString) match {
261
- // the presence of the empty string here is a signal that the symbol
262
- // is already complete and so instead of completing, we want to show
263
- // the user the method signature. there are various JLine 3 features
264
- // one might use to do this instead; sticking to basics for now
265
- case " " :: defStrings if defStrings.nonEmpty =>
266
- // specifics here are cargo-culted from Ammonite
319
+ for (cc <- result.candidates)
320
+ newCandidates.add(candidateForResult(cc))
321
+
322
+ val parsedLineWord = parsedLine.word()
323
+ result.candidates.filter(_.name == parsedLineWord) match {
324
+ case Nil =>
325
+ case exacts =>
267
326
lineReader.getTerminal.writer.println()
268
- for (cc <- result.candidates.tail )
269
- lineReader.getTerminal.writer.println(cc.defString )
327
+ for (cc <- exacts )
328
+ lineReader.getTerminal.writer.println(cc.declString() )
270
329
lineReader.callWidget(LineReader .REDRAW_LINE )
271
330
lineReader.callWidget(LineReader .REDISPLAY )
272
331
lineReader.getTerminal.flush()
273
- // normal completion
274
- case _ =>
275
- for (cc <- result.candidates)
276
- newCandidates.add(candidateForResult(cc))
277
332
}
278
333
}
279
334
}
0 commit comments