mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-11855 Run manage.py task improvements
Refactored to new architecture (Presenter / ChunkDriver).
This commit is contained in:
@@ -852,7 +852,14 @@ remote.interpreter.configure.temp.files.path.label=PyCharm helpers path:
|
||||
# Message we display for inspection if user uses custom class type members that do not exist
|
||||
custom.type.mimic.name=Dynamic class based on {0}
|
||||
|
||||
|
||||
# Values for command argument value validation
|
||||
commandsWithArgs.validation.badValue=Argument can't have this value
|
||||
commandsWithArgs.validation.excess=Excess argument
|
||||
commandsWithArgs.enterCommand.label=Enter command here
|
||||
commandLine.validation.badCommand=Unknown command
|
||||
commandLine.validation.argMissing=Required argument value is missing
|
||||
commandLine.validation.argBadValue=Argument can't have this value
|
||||
commandLine.validation.excessArg=Excess argument value
|
||||
# And for labels
|
||||
commandLine.subText.key.complete=Use {0} to complete selected variant
|
||||
commandLine.subText.key.suggestions=Use {0} to view available values
|
||||
commandLine.subText.key.executeUnknown=Click {0} to execute
|
||||
commandLine.subText.key.executeCommand=Click {0} to execute "{1}"
|
||||
|
||||
+12
-2
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.optParse;
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.util.Range;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -33,6 +33,7 @@ public final class WordWithPosition extends Range<Integer> {
|
||||
|
||||
/**
|
||||
* Creates word with beam (it has start, but it is infinite)
|
||||
*
|
||||
* @param word word
|
||||
* @param from start
|
||||
*/
|
||||
@@ -50,6 +51,15 @@ public final class WordWithPosition extends Range<Integer> {
|
||||
myWord = word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance with certain text and range (start/end)
|
||||
* @param word text
|
||||
* @param range range
|
||||
*/
|
||||
public WordWithPosition(@NotNull final String word, @NotNull final Range<Integer> range) {
|
||||
this(word, range.getFrom(), range.getTo());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getText() {
|
||||
return myWord;
|
||||
@@ -121,7 +131,7 @@ public final class WordWithPosition extends Range<Integer> {
|
||||
* @return parse result
|
||||
*/
|
||||
@NotNull
|
||||
static List<WordWithPosition> splitText(@NotNull final String text) {
|
||||
public static List<WordWithPosition> splitText(@NotNull final String text) {
|
||||
// TODO: Rewrite using regex or scanner?
|
||||
int position = 0;
|
||||
int wordStart = -1;
|
||||
@@ -27,9 +27,8 @@ public interface CommandInterfacePresenter extends Presenter {
|
||||
/**
|
||||
* Called by view when user types new text or text changed by some other reason
|
||||
*
|
||||
* @param inForcedTextMode text changed not by user but by calling {@link com.jetbrains.python.commandInterface.CommandInterfaceView#forceText(String)}
|
||||
*/
|
||||
void textChanged(boolean inForcedTextMode);
|
||||
void textChanged();
|
||||
|
||||
/**
|
||||
* Called by view when user requests for completion (like tab)
|
||||
@@ -39,14 +38,13 @@ public interface CommandInterfacePresenter extends Presenter {
|
||||
void completionRequested(@Nullable String valueFromSuggestionList);
|
||||
|
||||
/**
|
||||
* Called by view when user asks for suggestions (CTRL+Space)
|
||||
* Called by view when user asks for suggestions (like CTRL+Space)
|
||||
*/
|
||||
void suggestionRequested();
|
||||
|
||||
/**
|
||||
* Called by view when user wants to execute command (Enter is presed)
|
||||
* Called by view when user wants to execute command (like enter)
|
||||
*
|
||||
* @param valueFromSuggestionList value selected from suggestion list (if any selected)
|
||||
*/
|
||||
void executionRequested(@Nullable String valueFromSuggestionList);
|
||||
void executionRequested();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface;
|
||||
|
||||
import com.jetbrains.python.optParse.WordWithPosition;
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.suggestionList.SuggestionsBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -30,6 +31,13 @@ import java.util.List;
|
||||
*/
|
||||
public interface CommandInterfaceView {
|
||||
|
||||
/**
|
||||
* Special place in command line that represents "after the last character" place.
|
||||
* To be used in methods like {@link #setInfoAndErrors(java.util.Collection, java.util.Collection)} to mark it.
|
||||
*/
|
||||
@NotNull
|
||||
Range<Integer> AFTER_LAST_CHARACTER_RANGE = new Range<Integer>(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
|
||||
/**
|
||||
* Launches view
|
||||
*/
|
||||
@@ -39,43 +47,34 @@ public interface CommandInterfaceView {
|
||||
* Suggests user some elements (for completion reason)
|
||||
*
|
||||
* @param suggestions what to suggest (see {@link com.jetbrains.python.suggestionList.SuggestionsBuilder})
|
||||
* @param absolute display list in its main position, or directly near the text
|
||||
* @param toSelect word to select if list (if any)
|
||||
* @param absolute display list in its main position, or directly near the caret
|
||||
* @param toSelect word to select in list (if any)
|
||||
*/
|
||||
void displaySuggestions(@NotNull SuggestionsBuilder suggestions, boolean absolute, @Nullable String toSelect);
|
||||
|
||||
/**
|
||||
* Emphasize errors (like red line and special message).
|
||||
*
|
||||
* @param errors list of errors (coordinates and error message. Message may be empty not to display any text)
|
||||
* @param specialErrorPlace if you want to underline special place, you may provide it here
|
||||
*/
|
||||
void showErrors(@NotNull final List<WordWithPosition> errors, @Nullable SpecialErrorPlace specialErrorPlace);
|
||||
|
||||
/**
|
||||
* Change text to the one provided
|
||||
* Each time caret meets certain place, view should check whether some subtext has to be displayed.
|
||||
* There are 2 types of subtext to be displayed:
|
||||
* <ol>
|
||||
* <li>Suggestion Text: View says something like "click FOO to see list of suggestions". Only presenter knows exact places where
|
||||
* suggestions are available, so it should provide them</li>
|
||||
* <li>Default text: In all other cases view displays default text (if available).</li>
|
||||
* </ol>
|
||||
* <p/>
|
||||
* Presenter provides view list of special places
|
||||
*
|
||||
* @param newText text to display
|
||||
* @param defaultSubText default text
|
||||
* @param suggestionAvailablePlaces list of places where suggestions are available in format [from, to].
|
||||
*/
|
||||
void forceText(@NotNull String newText);
|
||||
|
||||
/**
|
||||
* Display text in sub part (like hint)
|
||||
*
|
||||
* @param subText text to display
|
||||
*/
|
||||
void setSubText(@NotNull String subText);
|
||||
void configureSubTexts(@Nullable String defaultSubText,
|
||||
@NotNull List<Range<Integer>> suggestionAvailablePlaces);
|
||||
|
||||
/**
|
||||
* Hide suggestion list
|
||||
*/
|
||||
void removeSuggestions();
|
||||
|
||||
/**
|
||||
* Displays baloon with message right under the last letter.
|
||||
*
|
||||
* @param message text to display
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return text, entered by user
|
||||
@@ -83,41 +82,40 @@ public interface CommandInterfaceView {
|
||||
@NotNull
|
||||
String getText();
|
||||
|
||||
|
||||
/**
|
||||
* Enlarges view to make it as big as required to display appropriate number of chars
|
||||
* When caret meets certain place, view may display some info and some errors.
|
||||
* Errors, how ever, may always be emphasized (with something like red line).
|
||||
* This function configures view with pack of ranges and texts to display.
|
||||
* Special place {@link #AFTER_LAST_CHARACTER_RANGE} may also be used.
|
||||
* Each place is described as start-end position (in chars) where it should be enabled.
|
||||
* Each call removes previously enabled information.
|
||||
*
|
||||
* @param widthInChars number of chars
|
||||
* @param errors places to be marked as errors with error text.
|
||||
* @param infoBalloons places to display info balloon
|
||||
* @see #AFTER_LAST_CHARACTER_RANGE
|
||||
*/
|
||||
void setPreferredWidthInChars(int widthInChars);
|
||||
void setInfoAndErrors(@NotNull final Collection<WordWithPosition> infoBalloons, @NotNull final Collection<WordWithPosition> errors);
|
||||
|
||||
|
||||
/**
|
||||
* Displays help balloon when cursor meets certain place.
|
||||
* Each balloon is described as start-end position (in chars) where it should be enabled
|
||||
* and test to display.
|
||||
* <strong>Caution: Each call removes previuos balloons!</strong>
|
||||
* Inserts text after caret moving next chars to the right
|
||||
*
|
||||
* @param balloons list of balloons to display (i.e. you want to text 'foo' be displayed when user sets cursor on position
|
||||
* from 1 to 3, so you add 'foo',1,4 here)
|
||||
* @param text text to insert
|
||||
*/
|
||||
void setBalloons(@NotNull final Collection<WordWithPosition> balloons);
|
||||
void insertTextAfterCaret(@NotNull String text);
|
||||
|
||||
/**
|
||||
* @return true if current caret position is on the word (no on whitespace)
|
||||
* Replaces current text with another one.
|
||||
*
|
||||
* @param from from
|
||||
* @param to to
|
||||
* @param newText text to replace
|
||||
*/
|
||||
boolean isCaretOnWord();
|
||||
|
||||
void replaceText(final int from, final int to, @NotNull String newText);
|
||||
|
||||
/**
|
||||
* Special place that may be underlined
|
||||
* @return position of caret (in chars)
|
||||
*/
|
||||
enum SpecialErrorPlace {
|
||||
/**
|
||||
* Whole text (from start to end)
|
||||
*/
|
||||
WHOLE_TEXT,
|
||||
/**
|
||||
* Only after last character
|
||||
*/
|
||||
AFTER_LAST_CHAR
|
||||
}
|
||||
int getCaretPosition();
|
||||
}
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Chunk and its info pair. Chunk may be null, while chunk info always present.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class ChunkAndInfo implements Comparable<ChunkAndInfo> {
|
||||
@Nullable
|
||||
private final WordWithPosition myChunk;
|
||||
@NotNull
|
||||
private final ChunkInfo myChunkInfo;
|
||||
|
||||
ChunkAndInfo(@Nullable final WordWithPosition chunk, @NotNull final ChunkInfo chunkInfo) {
|
||||
myChunk = chunk;
|
||||
myChunkInfo = chunkInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return chunk (word). may be null
|
||||
*/
|
||||
@Nullable
|
||||
WordWithPosition getChunk() {
|
||||
return myChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return chunk info.
|
||||
*/
|
||||
@NotNull
|
||||
ChunkInfo getChunkInfo() {
|
||||
return myChunkInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull final ChunkAndInfo o) {
|
||||
if (myChunk == null && o.myChunk == null) {
|
||||
return 0;
|
||||
}
|
||||
if (myChunk == null) {
|
||||
return 1;
|
||||
}
|
||||
if (o.myChunk == null) {
|
||||
return -1;
|
||||
}
|
||||
return myChunk.getFrom().compareTo(o.myChunk.getFrom());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ChunkAndInfo info = (ChunkAndInfo)o;
|
||||
|
||||
if (myChunk != null ? !myChunk.equals(info.myChunk) : info.myChunk != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myChunk != null ? myChunk.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Driver that knows how to parse pack of chunks into chunk info.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface ChunkDriver {
|
||||
/**
|
||||
* Parses chunks into pack of chunks. There <strong>always</strong> should be chunk+1 chunkInfos (one for the tail like
|
||||
* {@link com.jetbrains.python.commandInterface.CommandInterfaceView#AFTER_LAST_CHARACTER_RANGE}).
|
||||
* So, at least one chunk info should also exist!
|
||||
*
|
||||
* @param chunks chunks (parts of command line)
|
||||
* @return parse info with chunks info. Warning: do not return less chunk infos than chunks provided. That leads to runtime error
|
||||
*/
|
||||
@NotNull
|
||||
ParseInfo parse(@NotNull List<WordWithPosition> chunks);
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfacePresenterAdapter;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import com.jetbrains.python.suggestionList.SuggestionsBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
// TODO: Test
|
||||
|
||||
/**
|
||||
* Presenter that uses {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} to parse pack of chunks
|
||||
* to obtain {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo}.
|
||||
* Each chunk may be paired with certain chunk info. Such info tells presenter whether this chunk has info, error, suggestions and so on.
|
||||
* If caret situated far from chunks, then next neariest chunk should be found (see {@link #findNearestChunkAndInfo()}.
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo
|
||||
*/
|
||||
public final class ChunkDriverBasedPresenter extends CommandInterfacePresenterAdapter {
|
||||
|
||||
@NotNull
|
||||
private final ChunkDriver myChunkDriver;
|
||||
@NotNull
|
||||
private final SortedSet<ChunkAndInfo> myChunkAndInfos = new TreeSet<ChunkAndInfo>();
|
||||
@Nullable
|
||||
private Runnable myExecutor;
|
||||
|
||||
public ChunkDriverBasedPresenter(@NotNull final CommandInterfaceView view,
|
||||
@NotNull final ChunkDriver chunkDriver) {
|
||||
super(view);
|
||||
myChunkDriver = chunkDriver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
super.launch();
|
||||
reparseText(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textChanged() {
|
||||
reparseText(false);
|
||||
}
|
||||
|
||||
private void reparseText(final boolean skipSuggestions) {
|
||||
final List<WordWithPosition> chunks = WordWithPosition.splitText(myView.getText());
|
||||
final ParseInfo parseInfo = myChunkDriver.parse(chunks);
|
||||
myExecutor = parseInfo.getExecutor();
|
||||
final List<ChunkInfo> chunkInfos = parseInfo.getChunkInfo();
|
||||
assert chunkInfos.size() >= chunks.size() : "Driver did not return enough chunks";
|
||||
assert !chunkInfos.isEmpty() : "At least one chunk info should exist";
|
||||
myChunkAndInfos.clear();
|
||||
for (int i = 0; i < chunkInfos.size(); i++) {
|
||||
final ChunkInfo chunkInfo = chunkInfos.get(i);
|
||||
final WordWithPosition chunk = chunks.size() > i ? chunks.get(i) : null;
|
||||
myChunkAndInfos.add(new ChunkAndInfo(chunk, chunkInfo));
|
||||
}
|
||||
|
||||
|
||||
// configure Errors And Balloons
|
||||
|
||||
final Collection<WordWithPosition> infoBalloons = new ArrayList<WordWithPosition>();
|
||||
final Collection<WordWithPosition> errorBalloons = new ArrayList<WordWithPosition>();
|
||||
|
||||
|
||||
for (final ChunkAndInfo chunkInfoPair : myChunkAndInfos) {
|
||||
final ChunkInfo chunkInfo = chunkInfoPair.getChunkInfo();
|
||||
Range<Integer> chunk = chunkInfoPair.getChunk();
|
||||
if (chunk == null) {
|
||||
// After the last!
|
||||
chunk = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE;
|
||||
}
|
||||
final String error = chunkInfo.getError();
|
||||
if (error != null) {
|
||||
errorBalloons.add(new WordWithPosition(error, chunk));
|
||||
}
|
||||
final String info = chunkInfo.getInfoBalloon();
|
||||
if (info != null) {
|
||||
infoBalloons.add(new WordWithPosition(info, chunk));
|
||||
}
|
||||
}
|
||||
|
||||
myView.setInfoAndErrors(infoBalloons, errorBalloons);
|
||||
|
||||
|
||||
if (!skipSuggestions) {
|
||||
configureSuggestion(false);
|
||||
}
|
||||
|
||||
// Configure subtexts
|
||||
final List<Range<Integer>> placesWhereSuggestionAvailable = new ArrayList<Range<Integer>>();
|
||||
for (final ChunkAndInfo chunkAndInfo : myChunkAndInfos) {
|
||||
Range<Integer> chunk = chunkAndInfo.getChunk();
|
||||
// If some place has suggestions, then add it
|
||||
if (chunkAndInfo.getChunkInfo().getSuggestions() != null) {
|
||||
if (chunk == null) {
|
||||
// If there is no such chunk, that means we are after the last character, so use "special case" here
|
||||
//noinspection ReuseOfLocalVariable
|
||||
chunk = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE;
|
||||
}
|
||||
placesWhereSuggestionAvailable.add(chunk);
|
||||
}
|
||||
}
|
||||
myView.configureSubTexts(parseInfo.getStatusText(), placesWhereSuggestionAvailable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestionRequested() {
|
||||
configureSuggestion(true); // Show or hide
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays suggestions if needed.
|
||||
*
|
||||
* @param requestedExplicitly is suggesions where requested by user explicitly or not
|
||||
*/
|
||||
private void configureSuggestion(final boolean requestedExplicitly) {
|
||||
myView.removeSuggestions();
|
||||
final ChunkAndInfo chunkAndInfo = findNearestChunkAndInfo();
|
||||
final ChunkInfo chunkInfo = chunkAndInfo.getChunkInfo();
|
||||
final WordWithPosition chunk = chunkAndInfo.getChunk();
|
||||
|
||||
final SuggestionInfo suggestionInfo = chunkInfo.getSuggestions();
|
||||
if (suggestionInfo == null || (!suggestionInfo.isShowSuggestionsAutomatically() && !requestedExplicitly)) {
|
||||
return;
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (chunk != null && !requestedExplicitly) {
|
||||
filterLeaveOnlyMatching(suggestions, chunk.getText());
|
||||
}
|
||||
// TODO: Place to add history
|
||||
if (!suggestions.isEmpty()) {
|
||||
// No need to display empty suggestions
|
||||
myView
|
||||
.displaySuggestions(new SuggestionsBuilder(suggestions), suggestionInfo.isShowAbsolute(), (chunk == null ? null : chunk.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters collection of suggestions leaving only those starts with certain text.
|
||||
* @param suggestions list to filter
|
||||
* @param textToMatch leave only parts that start with this param
|
||||
*/
|
||||
private static void filterLeaveOnlyMatching(@NotNull final Iterable<String> suggestions, @NotNull final String textToMatch) {
|
||||
// TODO: use guava instead?
|
||||
final Iterator<String> iterator = suggestions.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (!iterator.next().startsWith(textToMatch)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the nearest chunk and info to use. It may or may not find chunk, but it should always provide some chunk info.
|
||||
*
|
||||
* @return nearest chunk info and, probably, chunk.
|
||||
*/
|
||||
@NotNull
|
||||
private ChunkAndInfo findNearestChunkAndInfo() {
|
||||
final int caretPosition = myView.getCaretPosition();
|
||||
|
||||
for (final ChunkAndInfo chunkAndInfo : myChunkAndInfos) {
|
||||
final Range<Integer> range = chunkAndInfo.getChunk();
|
||||
if (range != null && range.isWithin(caretPosition)) {
|
||||
return chunkAndInfo;
|
||||
}
|
||||
if (range != null && range.getFrom() > caretPosition) {
|
||||
return new ChunkAndInfo(null, chunkAndInfo.getChunkInfo());
|
||||
}
|
||||
}
|
||||
|
||||
return new ChunkAndInfo(null, myChunkAndInfos.last().getChunkInfo());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void completionRequested(@Nullable final String valueFromSuggestionList) {
|
||||
final ChunkAndInfo chunkAndInfo = findNearestChunkAndInfo();
|
||||
final WordWithPosition chunk = chunkAndInfo.getChunk();
|
||||
|
||||
if (valueFromSuggestionList != null) {
|
||||
// Just insert it
|
||||
if (chunk != null) { // If caret is on the chunk itself
|
||||
myView.replaceText(chunk.getFrom(), chunk.getTo(), valueFromSuggestionList);
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(valueFromSuggestionList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//User did not provide text no insert, do our best to find one
|
||||
|
||||
final ChunkInfo chunkInfo = chunkAndInfo.getChunkInfo();
|
||||
final SuggestionInfo suggestionInfo = chunkInfo.getSuggestions();
|
||||
if (suggestionInfo == null) {
|
||||
return; // No suggestion available for this chunk
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (chunk != null) {
|
||||
filterLeaveOnlyMatching(suggestions, chunk.getText());
|
||||
}
|
||||
if (suggestions.size() == 1) {
|
||||
// Exclusive!
|
||||
if (chunk != null) {
|
||||
myView.replaceText(chunk.getFrom(), chunk.getTo(), suggestions.get(0));
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(suggestions.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executionRequested() {
|
||||
if (myExecutor == null) {
|
||||
// TODO: Display error somehow
|
||||
}
|
||||
else {
|
||||
myExecutor.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Information about certain place in text, provided by chunk driver.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class ChunkInfo {
|
||||
@Nullable
|
||||
private final String myInfoBalloon;
|
||||
@Nullable
|
||||
private final String myError;
|
||||
@Nullable
|
||||
private final SuggestionInfo mySuggestions;
|
||||
|
||||
|
||||
public ChunkInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error) {
|
||||
this(infoBalloon, error, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param infoBalloon Info balloon to display when caret meets this place (null if display nothing)
|
||||
* @param error Error balloon to display when caret meets this place and underline text as error (null if no error)
|
||||
* @param suggestions list of suggestions available in this place (if any)
|
||||
*/
|
||||
public ChunkInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error,
|
||||
@Nullable final SuggestionInfo suggestions) {
|
||||
myInfoBalloon = infoBalloon;
|
||||
myError = error;
|
||||
mySuggestions = suggestions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Info balloon to display when caret meets this place (null if display nothing)
|
||||
*/
|
||||
@Nullable
|
||||
public String getInfoBalloon() {
|
||||
return myInfoBalloon;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Error balloon to display when caret meets this place and underline text as error (null if no error)
|
||||
*/
|
||||
@Nullable
|
||||
public String getError() {
|
||||
return myError;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return list of suggestions available in this place (if any)
|
||||
*/
|
||||
@Nullable
|
||||
public SuggestionInfo getSuggestions() {
|
||||
return mySuggestions;
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Pack of {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo} for certain chunks and other parsing info.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class ParseInfo {
|
||||
@Nullable
|
||||
private final String myStatusText;
|
||||
@NotNull
|
||||
private final List<ChunkInfo> myChunkInfo = new ArrayList<ChunkInfo>();
|
||||
@Nullable
|
||||
private final Runnable myExecutor;
|
||||
|
||||
/**
|
||||
* @param chunkInfo Chunk info should match chunks in 1-to-1 manner:
|
||||
* If "chunk1 chunk2 chunk3" were provided, you then need to return list where first chunkInfo matches first chunk ets.
|
||||
* And there also should be one more chunkInfo for tail.
|
||||
* @param statusText Status text {@link com.jetbrains.python.commandInterface.CommandInterfaceView view} may display.
|
||||
* @param executor Engine to process command-line execution
|
||||
*/
|
||||
public ParseInfo(@NotNull final Collection<ChunkInfo> chunkInfo,
|
||||
@Nullable final String statusText,
|
||||
@Nullable final Runnable executor) {
|
||||
myStatusText = statusText;
|
||||
myChunkInfo.addAll(chunkInfo);
|
||||
myExecutor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple parse info with out of status text and executor
|
||||
*
|
||||
* @param chunkInfo Chunk info (See {@link #ParseInfo(java.util.Collection, String, Runnable)}
|
||||
* @see #ParseInfo(java.util.Collection, String, Runnable)
|
||||
*/
|
||||
public ParseInfo(@NotNull final Collection<ChunkInfo> chunkInfo) {
|
||||
this(chunkInfo, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Status text {@link com.jetbrains.python.commandInterface.CommandInterfaceView view} may display.
|
||||
*/
|
||||
@Nullable
|
||||
String getStatusText() {
|
||||
return myStatusText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Engine to process command-line execution
|
||||
*/
|
||||
@Nullable
|
||||
Runnable getExecutor() {
|
||||
return myExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Chunk info should match chunks in 1-to-1 manner, and there also should be one more chunkInfo for tail (
|
||||
* see {@link #ParseInfo(java.util.Collection, String, Runnable) ctor} manual)
|
||||
*/
|
||||
@NotNull
|
||||
List<ChunkInfo> getChunkInfo() {
|
||||
return Collections.unmodifiableList(myChunkInfo);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information about suggestions
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class SuggestionInfo {
|
||||
@NotNull
|
||||
private final List<String> mySuggestions = new ArrayList<String>();
|
||||
private final boolean myShowSuggestionsAutomatically;
|
||||
private final boolean myShowAbsolute;
|
||||
|
||||
/**
|
||||
* @param showSuggestionsAutomatically true it suggestions should be displayed even if user did not ask for that
|
||||
* @param showAbsolute show suggestions at the absolute position (not relative to caret).
|
||||
* @param suggestions List of suggestions to display
|
||||
*/
|
||||
public SuggestionInfo(final boolean showSuggestionsAutomatically,
|
||||
final boolean showAbsolute,
|
||||
@NotNull final Collection<String> suggestions) {
|
||||
myShowSuggestionsAutomatically = showSuggestionsAutomatically;
|
||||
myShowAbsolute = showAbsolute;
|
||||
mySuggestions.addAll(suggestions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of suggestions to display
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getSuggestions() {
|
||||
return Collections.unmodifiableList(mySuggestions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true it suggestions should be displayed even if user did not ask for that
|
||||
*/
|
||||
public boolean isShowSuggestionsAutomatically() {
|
||||
return myShowSuggestionsAutomatically;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return show suggestions at the absolute position (not relative to caret).
|
||||
*/
|
||||
public boolean isShowAbsolute() {
|
||||
return myShowAbsolute;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2000-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandInterface.CommandInterfacePresenter} implementation based on ideas of <strong>chunk</strong>
|
||||
* and {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver}.
|
||||
* This presenter explodes command line into several parts or chunks.
|
||||
* Chunks then passed to {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver driver} and it returns
|
||||
* all information it has about each chunk. Presenter uses this information to display chunks correctly using view.
|
||||
* To use this package, {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} should be implemented.
|
||||
*
|
||||
* See {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriverBasedPresenter} as entry point
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
+9
-20
@@ -13,39 +13,28 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information about command {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument arguments} and their value
|
||||
* Information about command {@link Argument arguments} and their value
|
||||
* validation.
|
||||
* Check optparse manual, package info and {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument}
|
||||
* Check optparse manual, package info and {@link Argument}
|
||||
* manual for more info about arguments.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface ArgumentsInfo {
|
||||
/**
|
||||
* Returns argument by its position.
|
||||
* Returns argument by its position. It also returns hint whether argument is required or not.
|
||||
*
|
||||
* @param argumentPosition argument position
|
||||
* @return null if no argument value is available at this position. Returns argument otherwise.
|
||||
* @return null if no argument value is available at this position.
|
||||
* Returns argument otherwise. Boolean here should tell you if argument is required (command is invalid with out of it) or optional (
|
||||
* it is acceptible, but command can work with our of it)
|
||||
*/
|
||||
@Nullable
|
||||
Argument getArgument(int argumentPosition);
|
||||
|
||||
|
||||
/**
|
||||
* Validates argument <strong>values</strong>.
|
||||
* Values should be provided as list. I.e. for <pre>my_command foo bar</pre> there should be list of "foo, bar".
|
||||
*
|
||||
* @param argumentValuesToCheck values to check
|
||||
* @return validation result
|
||||
*/
|
||||
@NotNull
|
||||
ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List<String> argumentValuesToCheck);
|
||||
Pair<Boolean, Argument> getArgument(int argumentPosition);
|
||||
}
|
||||
+12
-2
@@ -13,8 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParseResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,8 +42,16 @@ public interface Command {
|
||||
|
||||
|
||||
/**
|
||||
* @return Information about command positional, unnamed {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument arguments} (not options!)
|
||||
* @return Information about command positional, unnamed {@link Argument arguments} (not options!)
|
||||
*/
|
||||
@NotNull
|
||||
ArgumentsInfo getArgumentsInfo();
|
||||
|
||||
/**
|
||||
* Execute command
|
||||
*
|
||||
* @param module module to execute command against
|
||||
* @param commandLine command's command line
|
||||
*/
|
||||
void execute(@NotNull final Module module, @NotNull final CommandLineParseResult commandLine);
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ParseInfo;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.SuggestionInfo;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParseResult;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParser;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePartType;
|
||||
import com.jetbrains.python.commandLineParser.MalformedCommandLineException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Chunk driver that uses pack of commands.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandBasedChunkDriver implements ChunkDriver {
|
||||
@NotNull
|
||||
private final CommandLineParser myCommandLineParser;
|
||||
@NotNull
|
||||
private final Map<String, Command> myCommands = new TreeMap<String, Command>(); // To sort commands by name
|
||||
@NotNull
|
||||
private final Module myModule;
|
||||
|
||||
/**
|
||||
* @param commandLineParser parser to use
|
||||
* @param module module parsing takes place in
|
||||
* @param commands available commands
|
||||
*/
|
||||
public CommandBasedChunkDriver(@NotNull final CommandLineParser commandLineParser,
|
||||
@NotNull final Module module,
|
||||
@NotNull final Collection<? extends Command> commands) {
|
||||
myCommandLineParser = commandLineParser;
|
||||
for (final Command command : commands) {
|
||||
myCommands.put(command.getName(), command);
|
||||
}
|
||||
myModule = module;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ParseInfo parse(@NotNull final List<WordWithPosition> chunks) {
|
||||
// TODO: Refactor to add command first to prevent copy/paste
|
||||
if (chunks.isEmpty()) {
|
||||
return createBadCommandInfo(chunks.size());
|
||||
}
|
||||
|
||||
try {
|
||||
final CommandLineParseResult commandLine = myCommandLineParser.parse(chunks);
|
||||
final Command command = myCommands.get(commandLine.getCommand().getText());
|
||||
if (command == null) {
|
||||
// Bad command inserted
|
||||
return createBadCommandInfo(chunks.size());
|
||||
}
|
||||
|
||||
// Command exists, lets check its arguments
|
||||
|
||||
// TODO: Support options as well
|
||||
|
||||
// First, validate values
|
||||
final ArgumentsInfo commandArgumentsInfo = command.getArgumentsInfo();
|
||||
|
||||
|
||||
final List<ChunkInfo> chunkInfo = new ArrayList<ChunkInfo>();
|
||||
// First chunk iscommand and it seems to be ok
|
||||
chunkInfo.add(new ChunkInfo(null, null, new SuggestionInfo(false, true, myCommands.keySet())));
|
||||
|
||||
|
||||
// Now add balloons, info and suggestions
|
||||
for (int i = 0; i < commandLine.getParts().size(); i++) {
|
||||
final Pair<Boolean, Argument> argumentPair = commandArgumentsInfo.getArgument(i);
|
||||
if (argumentPair == null) { // Excess argument!
|
||||
chunkInfo.add(new ChunkInfo(null, PyBundle.message("commandLine.validation.excessArg")));
|
||||
continue;
|
||||
}
|
||||
final Argument argument = argumentPair.getSecond();
|
||||
final List<String> availableValues = argument.getAvailableValues();
|
||||
final Pair<CommandLinePartType, WordWithPosition> part = commandLine.getParts().get(i);
|
||||
if (part.first != CommandLinePartType.ARGUMENT) {
|
||||
// Only arguments are supported now, so we have nothing to say about this chunk
|
||||
chunkInfo.add(new ChunkInfo(null, null));
|
||||
}
|
||||
final String argumentValue = part.second.getText();
|
||||
String errorMessage = null;
|
||||
if (availableValues != null && !availableValues.contains(argumentValue)) {
|
||||
// Bad value
|
||||
errorMessage = PyBundle.message("commandLine.validation.argBadValue");
|
||||
}
|
||||
// Argument seems to be ok. We suggest values automatically only if value is bad
|
||||
chunkInfo.add(new ChunkInfo(argument.getHelpText(), errorMessage,
|
||||
(availableValues != null ? new SuggestionInfo(errorMessage != null, false, availableValues) : null)));
|
||||
}
|
||||
|
||||
|
||||
final Pair<Boolean, Argument> nextArgumentPair = commandArgumentsInfo.getArgument(commandLine.getParts().size());
|
||||
if (nextArgumentPair != null) {
|
||||
// Next arg exists
|
||||
final Argument nextArgument = nextArgumentPair.getSecond();
|
||||
final List<String> availableValues = nextArgument.getAvailableValues();
|
||||
// Only add error if required
|
||||
final String error = nextArgumentPair.first ? PyBundle.message("commandLine.validation.argMissing") : null;
|
||||
final ChunkInfo lastArgInfo =
|
||||
new ChunkInfo(nextArgument.getHelpText(), error,
|
||||
(availableValues != null ? new SuggestionInfo(false, false, availableValues) : null));
|
||||
chunkInfo.add(lastArgInfo);
|
||||
}
|
||||
else {
|
||||
// Looks like all arguments are satisfied. Adding empty chunk to prevent completion etc.
|
||||
// This is a hack, but with out of it last chunkinfo will always be used, even 200 chars after last place
|
||||
chunkInfo.add(new ChunkInfo(null, null));
|
||||
}
|
||||
|
||||
assert chunkInfo.size() >= chunks.size() : "Contract broken: not enough chunks";
|
||||
|
||||
return new ParseInfo(chunkInfo, command.getHelp(), new MyExecutor(command, commandLine));
|
||||
}
|
||||
catch (final MalformedCommandLineException ignored) {
|
||||
// Junk enetered!
|
||||
return createBadCommandInfo(chunks.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates parse info signaling command is bad or junk
|
||||
*
|
||||
* @param numberOfChunks number of chunks provided by user (we must return chunk info for each chunk + 1, accroding to contract)
|
||||
* @return parse info to return
|
||||
*/
|
||||
@NotNull
|
||||
private ParseInfo createBadCommandInfo(final int numberOfChunks) {
|
||||
final List<ChunkInfo> result = new ArrayList<ChunkInfo>();
|
||||
// We know that first chunk command line, but we can't say anything about outher chunks except they are bad.
|
||||
// How ever, we must say something according to contract (number of infos should be equal or greater than number of chunks)
|
||||
result
|
||||
.add(new ChunkInfo(null, PyBundle.message("commandLine.validation.badCommand"), new SuggestionInfo(true, true, myCommands.keySet())));
|
||||
for (int i = 1; i < numberOfChunks; i++) {
|
||||
result.add(
|
||||
new ChunkInfo(null, PyBundle.message("commandLine.validation.badCommand")));
|
||||
}
|
||||
|
||||
return new ParseInfo(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapter that executes command using {@link Command#execute(com.intellij.openapi.module.Module, com.jetbrains.python.commandLineParser.CommandLineParseResult)}
|
||||
*/
|
||||
private class MyExecutor implements Runnable {
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
@NotNull
|
||||
private final CommandLineParseResult myCommandLine;
|
||||
|
||||
MyExecutor(@NotNull final Command command, @NotNull final CommandLineParseResult line) {
|
||||
myCommand = command;
|
||||
myCommandLine = line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
myCommand.execute(myModule, myCommandLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-29
@@ -13,18 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsValuesValidationInfo.ArgumentValueError;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* In some special cases we have insight about command arguments.
|
||||
@@ -61,6 +59,15 @@ public final class KnownArgumentsInfo implements ArgumentsInfo {
|
||||
*/
|
||||
private final int myMaxArguments;
|
||||
|
||||
/**
|
||||
* For command with fixed number of arguments. In this case all arguments are fixed and required!
|
||||
*
|
||||
* @param arguments arguments this command have
|
||||
*/
|
||||
public KnownArgumentsInfo(@NotNull final Collection<Argument> arguments) {
|
||||
this(arguments, arguments.size(), arguments.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* For commands with infinite number of values last argument accepts (my_command VAL1 VAL2 .. VALN)
|
||||
*
|
||||
@@ -91,36 +98,20 @@ public final class KnownArgumentsInfo implements ArgumentsInfo {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Argument getArgument(final int argumentPosition) {
|
||||
public Pair<Boolean, Argument> getArgument(final int argumentPosition) {
|
||||
if (argumentPosition >= myMaxArguments) {
|
||||
return null;
|
||||
}
|
||||
final boolean optional = argumentPosition >= myMinArguments;
|
||||
|
||||
if (myArguments.size() > argumentPosition) {
|
||||
return myArguments.get(argumentPosition);
|
||||
return Pair.create(!optional, myArguments.get(argumentPosition));
|
||||
}
|
||||
|
||||
// We may need last one
|
||||
if (argumentPosition <= myMaxArguments) {
|
||||
return myArguments.get(myArguments.size() - 1);
|
||||
if (argumentPosition < myMaxArguments) {
|
||||
return Pair.create(false, myArguments.get(myArguments.size() - 1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List<String> argumentValuesToCheck) {
|
||||
final Map<Integer, ArgumentValueError> errors = new HashMap<Integer, ArgumentValueError>();
|
||||
|
||||
for (int i = 0; i < argumentValuesToCheck.size(); i++) {
|
||||
final String userValue = argumentValuesToCheck.get(i);
|
||||
final Argument argument = getArgument(i);
|
||||
if (argument == null) {
|
||||
errors.put(i, ArgumentValueError.EXCESS);
|
||||
continue;
|
||||
}
|
||||
final List<String> availableValues = argument.getAvailableValues();
|
||||
if (availableValues != null && !availableValues.contains(userValue)) {
|
||||
errors.put(i, ArgumentValueError.BAD_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArgumentsValuesValidationInfo(errors, argumentValuesToCheck.size() < myMinArguments);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* TODO: Redundant?!
|
||||
* Case when command has no arguments (for sure!)
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class NoArgumentsInfo implements ArgumentsInfo {
|
||||
/**
|
||||
* Instance to use when command has no arguments
|
||||
*/
|
||||
public static final ArgumentsInfo INSTANCE = new NoArgumentsInfo();
|
||||
|
||||
private NoArgumentsInfo() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<Boolean, Argument> getArgument(final int argumentPosition) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+5
-11
@@ -13,13 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* For many commands we know nothing about arguments but their help text.
|
||||
* This strategy is for this case
|
||||
@@ -40,15 +39,10 @@ public final class UnknownArgumentsInfo implements ArgumentsInfo {
|
||||
myHelp = allArgumentsHelpText;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Argument getArgument(final int argumentPosition) {
|
||||
return new Argument(myHelp); // We can't say argument does not exist.
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List<String> argumentValuesToCheck) {
|
||||
return ArgumentsValuesValidationInfo.NO_ERROR; // Actually, we have no idea
|
||||
public Pair<Boolean, Argument> getArgument(final int argumentPosition) {
|
||||
return Pair.create(false, new Argument(myHelp));
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2000-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} implementation based on idea of
|
||||
* {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.Command command} and its {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.Argument arguments}.
|
||||
*
|
||||
* See {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.CommandBasedChunkDriver} as entry point.
|
||||
* It parses command line using {@link com.jetbrains.python.commandLineParser.CommandLineParser} and finds matching command and arguments
|
||||
* provided by user
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Information about {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument arguments} values validation
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class ArgumentsValuesValidationInfo {
|
||||
/**
|
||||
* Validation with out of any error
|
||||
*/
|
||||
@NotNull
|
||||
static final ArgumentsValuesValidationInfo
|
||||
NO_ERROR = new ArgumentsValuesValidationInfo(Collections.<Integer, ArgumentValueError>emptyMap(), false);
|
||||
|
||||
private final Map<Integer, ArgumentValueError> myPositionOfErrorArguments = new HashMap<Integer, ArgumentValueError>();
|
||||
private final boolean myNotEnoughArguments;
|
||||
|
||||
/**
|
||||
* @param positionOfErrorArguments map of [argument_position, its_value_error]
|
||||
* @param notEnoughArguments true if not enough arguments values provided (i.e. some required arg missed)
|
||||
*/
|
||||
ArgumentsValuesValidationInfo(@NotNull final Map<Integer, ArgumentValueError> positionOfErrorArguments,
|
||||
final boolean notEnoughArguments) {
|
||||
myPositionOfErrorArguments.putAll(positionOfErrorArguments);
|
||||
myNotEnoughArguments = notEnoughArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return map of [argument_position, its_value_error]
|
||||
*/
|
||||
@NotNull
|
||||
Map<Integer, ArgumentValueError> getPositionOfErrorArguments() {
|
||||
return Collections.unmodifiableMap(myPositionOfErrorArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if not enough argument values provided (i.e. some required arg missed)
|
||||
*/
|
||||
boolean isNotEnoughArguments() {
|
||||
return myNotEnoughArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of argument value error.
|
||||
*/
|
||||
enum ArgumentValueError {
|
||||
/**
|
||||
* This argument is redundant
|
||||
*/
|
||||
EXCESS,
|
||||
/**
|
||||
* Argument has bad value
|
||||
*/
|
||||
BAD_VALUE
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Simple command implementation
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class CommandAdapter implements Command {
|
||||
@NotNull
|
||||
private final String myName;
|
||||
@Nullable
|
||||
private final String myHelp;
|
||||
@NotNull
|
||||
private final ArgumentsInfo myArgumentsInfo;
|
||||
|
||||
/**
|
||||
* @param help help text
|
||||
* @param name command name
|
||||
* @param argumentsInfo arguments info
|
||||
*/
|
||||
public CommandAdapter(@NotNull final String name, @Nullable final String help, @NotNull ArgumentsInfo argumentsInfo) {
|
||||
myName = name;
|
||||
myHelp = help;
|
||||
myArgumentsInfo = argumentsInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command name
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public final String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final String getHelp() {
|
||||
return myHelp;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public final ArgumentsInfo getArgumentsInfo() {
|
||||
return myArgumentsInfo;
|
||||
}
|
||||
}
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information about command and its arguments to save in history / pass to execution etc.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class CommandExecutionInfo {
|
||||
/**
|
||||
* Command and arguments separator.
|
||||
*/
|
||||
private static final String SEPARATOR = " ";
|
||||
@NotNull
|
||||
private final String myCommandName;
|
||||
@NotNull
|
||||
private final String[] myArguments;
|
||||
|
||||
/**
|
||||
* @param commandName command
|
||||
* @param arguments its arguments
|
||||
*/
|
||||
public CommandExecutionInfo(@NotNull final String commandName, @NotNull final String... arguments) {
|
||||
myCommandName = commandName;
|
||||
myArguments = arguments.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command
|
||||
*/
|
||||
@NotNull
|
||||
public String getCommandName() {
|
||||
return myCommandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command arguments
|
||||
*/
|
||||
@NotNull
|
||||
public String[] getArguments() {
|
||||
return myArguments.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command in format "command arg1 arg2". Opposite to {@link #fromString(String)}
|
||||
* @see #fromString(String)
|
||||
*/
|
||||
@NotNull
|
||||
public String toString() {
|
||||
// TODO: What if command or argument has space in it? Escape somehow!
|
||||
return StringUtil.join(ArrayUtil.mergeArrays(new String[]{myCommandName}, myArguments), SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stringToUnserialize string created by {@link #toString()}
|
||||
* @return command parsed from string
|
||||
* @see #toString()
|
||||
*/
|
||||
@Nullable
|
||||
public static CommandExecutionInfo fromString(@NotNull final String stringToUnserialize) {
|
||||
// TODO: What if command or argument has space in it? Escape somehow!
|
||||
final List<String> strings = StringUtil.split(stringToUnserialize, SEPARATOR);
|
||||
if (strings.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new CommandExecutionInfo(strings.get(0), ArrayUtil.toStringArray(strings.subList(1, strings.size())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CommandExecutionInfo)) return false;
|
||||
|
||||
CommandExecutionInfo info = (CommandExecutionInfo)o;
|
||||
|
||||
if (!Arrays.equals(myArguments, info.myArguments)) return false;
|
||||
if (!myCommandName.equals(info.myCommandName)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myCommandName.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(myArguments);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
-247
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfacePresenterAdapter;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace;
|
||||
import com.jetbrains.python.optParse.MalformedCommandLineException;
|
||||
import com.jetbrains.python.optParse.ParsedCommandLine;
|
||||
import com.jetbrains.python.optParse.WordWithPosition;
|
||||
import com.jetbrains.python.suggestionList.SuggestionsBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Command-line interface presenter that is command-based
|
||||
*
|
||||
* @param <C> Command type
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class CommandInterfacePresenterCommandBased<C extends Command> extends CommandInterfacePresenterAdapter {
|
||||
/**
|
||||
* [name] -> command. Linked is used to preserve order.
|
||||
*/
|
||||
private final Map<String, C> myCommands = new LinkedHashMap<String, C>();
|
||||
/**
|
||||
* currenly used strategy (see interface for more info)
|
||||
*/
|
||||
private Strategy myStrategy;
|
||||
|
||||
|
||||
/**
|
||||
* @param view view
|
||||
* @param commands available commands
|
||||
*/
|
||||
public CommandInterfacePresenterCommandBased(@NotNull final CommandInterfaceView view,
|
||||
@NotNull final Iterable<C> commands) {
|
||||
super(view);
|
||||
for (final C command : commands) {
|
||||
myCommands.put(command.getName(), command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param view view
|
||||
* @param commands available commands
|
||||
*/
|
||||
public CommandInterfacePresenterCommandBased(@NotNull final CommandInterfaceView view,
|
||||
@NotNull final C... commands) {
|
||||
this(view, Arrays.asList(commands));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
/*myView.setPreferredWidthInChars(getMaximumCommandWithArgsLength());*/
|
||||
super.launch();
|
||||
myStrategy = new NoCommandStrategy(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textChanged(final boolean inForcedTextMode) {
|
||||
configureStrategy();
|
||||
myView.setSubText(myStrategy.getSubText());
|
||||
final Pair<SpecialErrorPlace, List<WordWithPosition>> errorInfo = myStrategy.getErrorInfo();
|
||||
myView.showErrors(errorInfo.getSecond(), errorInfo.first);
|
||||
myView.setBalloons(myStrategy.getBalloonsToShow());
|
||||
|
||||
final SuggestionInfo suggestionInfo = myStrategy.getSuggestionInfo();
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
|
||||
final String lastPart = getLastPart();
|
||||
if ((lastPart != null) && myStrategy.isUnknownTextExists()) {
|
||||
//Filter to starts from
|
||||
final Iterator<String> iterator = suggestions.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final String textToCheck = iterator.next();
|
||||
|
||||
if (!textToCheck.startsWith(lastPart)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!suggestionInfo.myShowOnlyWhenRequested && !suggestions.isEmpty()) {
|
||||
final SuggestionsBuilder suggestionsBuilder = getBuilderWithHistory();
|
||||
suggestionsBuilder.add(suggestions);
|
||||
|
||||
myView
|
||||
.displaySuggestions(suggestionsBuilder, suggestionInfo.myAbsolute, null);
|
||||
}
|
||||
else {
|
||||
myView.removeSuggestions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return builder that already has history in its prefix group (see {@link com.jetbrains.python.suggestionList.SuggestionsBuilder})
|
||||
*/
|
||||
@NotNull
|
||||
private SuggestionsBuilder getBuilderWithHistory() {
|
||||
return new SuggestionsBuilder();
|
||||
|
||||
// TODO: Uncomment when history would be fixed
|
||||
/*final SuggestionsBuilder suggestionsBuilder = new SuggestionsBuilder();
|
||||
final List<CommandExecutionInfo> history = getHistory();
|
||||
final Collection<String> historyCommands = new LinkedHashSet<String>();
|
||||
for (final CommandExecutionInfo info : history) {
|
||||
historyCommands.add(info.toString());
|
||||
}
|
||||
|
||||
if (!historyCommands.isEmpty()) {
|
||||
// TODO: Later implement folding by name
|
||||
suggestionsBuilder.changeGroup(false);
|
||||
suggestionsBuilder
|
||||
.add(ArrayUtil.toStringArray(historyCommands));
|
||||
suggestionsBuilder.changeGroup(true);
|
||||
}
|
||||
|
||||
return suggestionsBuilder;*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @return execution info from history. It is empty by default, child should implement it.
|
||||
*/
|
||||
@NotNull
|
||||
protected List<CommandExecutionInfo> getHistory() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command that entered in box, or null of just entered
|
||||
*/
|
||||
@Nullable
|
||||
protected CommandExecutionInfo getCommandToExecute() {
|
||||
return myStrategy.getCommandToExecute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and sets appropriate strategy
|
||||
*/
|
||||
private void configureStrategy() {
|
||||
final ParsedCommandLine line = getParsedCommandLine();
|
||||
if (line != null) {
|
||||
final Command command = myCommands.get(line.getCommand().getText());
|
||||
if (command != null) {
|
||||
myStrategy = new InCommandStrategy(command, line, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
myStrategy = new NoCommandStrategy(this); // No command or bad command found
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completionRequested(@Nullable final String valueFromSuggestionList) {
|
||||
if (valueFromSuggestionList != null) {
|
||||
final SuggestionInfo suggestionInfo = myStrategy.getSuggestionInfo();
|
||||
if (suggestionInfo.getSuggestions().contains(valueFromSuggestionList)) {
|
||||
final ParsedCommandLine commandLine = getParsedCommandLine();
|
||||
final List<String> words = commandLine != null ? commandLine.getAsWords() : new ArrayList<String>();
|
||||
if (!words.isEmpty() && myView.isCaretOnWord()) {
|
||||
words.remove(words.size() - 1);
|
||||
}
|
||||
words.add(valueFromSuggestionList);
|
||||
myView.forceText(StringUtil.join(words, " "));
|
||||
}
|
||||
}
|
||||
myView.removeSuggestions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestionRequested() {
|
||||
final SuggestionInfo suggestionInfo = myStrategy.getSuggestionInfo();
|
||||
final List<String> suggestions = suggestionInfo.getSuggestions();
|
||||
if (!suggestions.isEmpty()) {
|
||||
final SuggestionsBuilder suggestionsBuilder = getBuilderWithHistory();
|
||||
suggestionsBuilder.add(suggestions);
|
||||
myView.displaySuggestions(suggestionsBuilder, suggestionInfo.myAbsolute, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executionRequested(@Nullable final String valueFromSuggestionList) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [command_name => command] all available commands
|
||||
*/
|
||||
@NotNull
|
||||
protected final Map<String, C> getCommands() {
|
||||
return Collections.unmodifiableMap(myCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return parsed commandline entered by user
|
||||
*/
|
||||
@Nullable
|
||||
final ParsedCommandLine getParsedCommandLine() {
|
||||
try {
|
||||
return new ParsedCommandLine(myView.getText());
|
||||
}
|
||||
catch (final MalformedCommandLineException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return last part of splitted text (if any). I.e. "foo bar spam" will return "spam"
|
||||
*/
|
||||
@Nullable
|
||||
final String getLastPart() {
|
||||
final ParsedCommandLine commandLine = getParsedCommandLine();
|
||||
if (commandLine == null || commandLine.getAsWords().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final List<String> words = commandLine.getAsWords();
|
||||
return words.get(words.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return view
|
||||
*/
|
||||
@NotNull
|
||||
CommandInterfaceView getView() {
|
||||
return myView;
|
||||
}
|
||||
|
||||
}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace;
|
||||
import com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsValuesValidationInfo.ArgumentValueError;
|
||||
import com.jetbrains.python.optParse.ParsedCommandLine;
|
||||
import com.jetbrains.python.optParse.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Strategy implementation for case when user entered command
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class InCommandStrategy extends Strategy {
|
||||
@NotNull
|
||||
private final List<String> myArguments = new ArrayList<String>();
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
@NotNull
|
||||
private final ParsedCommandLine myCommandLine;
|
||||
|
||||
/**
|
||||
* @param command command enrtered by user
|
||||
* @param presenter presenter
|
||||
*/
|
||||
InCommandStrategy(@NotNull final Command command,
|
||||
@NotNull final ParsedCommandLine commandLine,
|
||||
@NotNull final CommandInterfacePresenterCommandBased<?> presenter) {
|
||||
super(presenter);
|
||||
myArguments.addAll(WordWithPosition.fetchText(commandLine.getArguments()));
|
||||
myCommand = command;
|
||||
myCommandLine = commandLine;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSubText() {
|
||||
final String help = myCommand.getHelp();
|
||||
if (help != null) {
|
||||
return help;
|
||||
}
|
||||
return "Place to display help";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
SuggestionInfo getSuggestionInfo() {
|
||||
final Argument nextArgument = myCommand.getArgumentsInfo().getArgument(myCommandLine.getArguments().size());
|
||||
if (nextArgument != null) {// TODO: Check options!
|
||||
// If next arg exists
|
||||
final List<String> availableValues = nextArgument.getAvailableValues();
|
||||
if (availableValues != null) { // If has available values
|
||||
return new SuggestionInfo(false, false, availableValues);
|
||||
}
|
||||
}
|
||||
return new SuggestionInfo(false, false, Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
List<WordWithPosition> getBalloonsToShow() {
|
||||
// Display argument balloons right from command end to last argument end
|
||||
final ArgumentsInfo argumentsInfo = myCommand.getArgumentsInfo();
|
||||
final List<WordWithPosition> arguments = myCommandLine.getArguments();
|
||||
if (arguments.isEmpty()) {
|
||||
// If no arguments provided, then display first argument popup right after command
|
||||
final Argument firstArgument = argumentsInfo.getArgument(0);
|
||||
if (firstArgument == null) {
|
||||
return Collections.emptyList(); // Looks like no argument required
|
||||
}
|
||||
return Collections
|
||||
.singletonList(new WordWithPosition(firstArgument.getHelpText(), myCommandLine.getCommand().getTo() + 1));
|
||||
}
|
||||
|
||||
final List<WordWithPosition> result = new ArrayList<WordWithPosition>(arguments.size());
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
final WordWithPosition argEnteredByUser = arguments.get(i);
|
||||
final Argument argument = argumentsInfo.getArgument(i);
|
||||
if (argument != null) {
|
||||
// Display argument help
|
||||
result.add(argEnteredByUser.copyWithDifferentText(argument.getHelpText()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isUnknownTextExists() {
|
||||
if (myCommandLine.getAsWords().isEmpty()) {
|
||||
return false; // Command only
|
||||
}
|
||||
final String lastPart = myPresenter.getLastPart();
|
||||
return ((lastPart != null) && !getSuggestionInfo().getSuggestions().contains(lastPart));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
CommandExecutionInfo getCommandToExecute() {
|
||||
return new CommandExecutionInfo(myCommand.getName(), ArrayUtil.toStringArray(myArguments));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
Pair<SpecialErrorPlace, List<WordWithPosition>> getErrorInfo() {
|
||||
final List<WordWithPosition> userProvidedValues = myCommandLine.getArguments();
|
||||
SpecialErrorPlace specialError = null;
|
||||
final List<WordWithPosition> errors = new ArrayList<WordWithPosition>();
|
||||
|
||||
final ArgumentsValuesValidationInfo validation =
|
||||
myCommand.getArgumentsInfo().validateArgumentValues(WordWithPosition.fetchText(userProvidedValues));
|
||||
if (validation.isNotEnoughArguments()) {
|
||||
specialError = SpecialErrorPlace.AFTER_LAST_CHAR;
|
||||
}
|
||||
for (final Entry<Integer, ArgumentValueError> errorEntry : validation.getPositionOfErrorArguments().entrySet()) {
|
||||
final String errorText = (errorEntry.getValue() == ArgumentValueError.BAD_VALUE ?
|
||||
PyBundle.message("commandsWithArgs.validation.badValue") :
|
||||
PyBundle.message("commandsWithArgs.validation.excess") );
|
||||
errors.add(userProvidedValues.get(errorEntry.getKey()).copyWithDifferentText(errorText));
|
||||
}
|
||||
|
||||
|
||||
return Pair.create(specialError, errors);
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import com.google.common.collect.ContiguousSet;
|
||||
import com.google.common.collect.DiscreteDomain;
|
||||
import com.google.common.collect.Range;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsValuesValidationInfo.ArgumentValueError;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Case when command has no arguments (for sure!)
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class NoArgumentsInfo implements ArgumentsInfo {
|
||||
/**
|
||||
* Instance to use when command has no arguments
|
||||
*/
|
||||
public static final ArgumentsInfo INSTANCE = new NoArgumentsInfo();
|
||||
|
||||
private NoArgumentsInfo() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Argument getArgument(final int argumentPosition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List<String> argumentValuesToCheck) {
|
||||
if (argumentValuesToCheck.isEmpty()) {
|
||||
return ArgumentsValuesValidationInfo.NO_ERROR;
|
||||
}
|
||||
final Map<Integer, ArgumentValueError> errors =
|
||||
new HashMap<Integer, ArgumentsValuesValidationInfo.ArgumentValueError>();
|
||||
for (final int errorPosition : ContiguousSet.create(Range.closedOpen(0, argumentValuesToCheck.size()), DiscreteDomain.integers())) {
|
||||
errors.put(errorPosition, ArgumentValueError.EXCESS);
|
||||
}
|
||||
return new ArgumentsValuesValidationInfo(errors, false);
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace;
|
||||
import com.jetbrains.python.optParse.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Strategy implementation for case when no command parsed
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class NoCommandStrategy extends Strategy {
|
||||
|
||||
private static final Pair<SpecialErrorPlace, List<WordWithPosition>>
|
||||
NO_ERROR = Pair.create(null, Collections.<WordWithPosition>emptyList());
|
||||
|
||||
NoCommandStrategy(@NotNull final CommandInterfacePresenterCommandBased<?> presenter) {
|
||||
super(presenter);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
String getSubText() {
|
||||
return PyBundle.message("commandsWithArgs.enterCommand.label");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
SuggestionInfo getSuggestionInfo() {
|
||||
return new SuggestionInfo(true, isTextBoxEmpty(), new ArrayList<String>(myPresenter.getCommands().keySet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isUnknownTextExists() {
|
||||
return !myPresenter.getView().getText().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
Pair<SpecialErrorPlace, List<WordWithPosition>> getErrorInfo() {
|
||||
// No error if textbox empty, but mark everything as error if some text entered: it is junk (it can't be command,
|
||||
// InCommand strategy were selected otherwise)
|
||||
return isTextBoxEmpty() ? NO_ERROR : Pair.create(SpecialErrorPlace.WHOLE_TEXT, Collections.<WordWithPosition>emptyList());
|
||||
}
|
||||
|
||||
private boolean isTextBoxEmpty() {
|
||||
return myPresenter.getView().getText().isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
CommandExecutionInfo getCommandToExecute() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace;
|
||||
import com.jetbrains.python.optParse.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Strategy that helps {@link com.jetbrains.python.commandInterface.commandsWithArgs.CommandInterfacePresenterCommandBased}
|
||||
* to deal with 2 states: when no text entered (or some junk enetered) or command name entered
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
abstract class Strategy {
|
||||
@NotNull
|
||||
protected final CommandInterfacePresenterCommandBased<?> myPresenter;
|
||||
|
||||
/**
|
||||
* @param presenter presenter
|
||||
*/
|
||||
protected Strategy(@NotNull final CommandInterfacePresenterCommandBased<?> presenter) {
|
||||
myPresenter = presenter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return sub text to display
|
||||
*/
|
||||
@NotNull
|
||||
abstract String getSubText();
|
||||
|
||||
/**
|
||||
* @return suggestions
|
||||
*/
|
||||
@NotNull
|
||||
abstract SuggestionInfo getSuggestionInfo();
|
||||
|
||||
// TODO: Merge baloon and error (actually the same)
|
||||
@NotNull
|
||||
List<WordWithPosition> getBalloonsToShow() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command that entered in box, or null of just entered
|
||||
*/
|
||||
@Nullable
|
||||
abstract CommandExecutionInfo getCommandToExecute();
|
||||
|
||||
|
||||
/**
|
||||
* @return errors
|
||||
*/
|
||||
@NotNull
|
||||
abstract Pair<SpecialErrorPlace, List<WordWithPosition>> getErrorInfo();
|
||||
|
||||
/**
|
||||
* @return if text entered by user contains some unknown commands
|
||||
*/
|
||||
abstract boolean isUnknownTextExists();
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information abouyt suggestion, provided by {@link com.jetbrains.python.commandInterface.commandsWithArgs.Strategy}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
@SuppressWarnings("PackageVisibleField")
|
||||
// No do not need to hide field: everything is internal API in package, anyway
|
||||
final class SuggestionInfo {
|
||||
/**
|
||||
* Suggestions
|
||||
*/
|
||||
private final List<String> mySuggestions = new ArrayList<String>();
|
||||
/**
|
||||
* Display them at absolute location or relative to last letter
|
||||
*/
|
||||
final boolean myAbsolute;
|
||||
/**
|
||||
* Show then any time, or only when user requests them
|
||||
*/
|
||||
final boolean myShowOnlyWhenRequested;
|
||||
|
||||
/**
|
||||
* @param absolute Display them at absolute location or relative to last letter
|
||||
* @param showOnlyWhenRequested Show then any time, or only when user requests them
|
||||
* @param suggestions Suggestions
|
||||
*/
|
||||
SuggestionInfo(final boolean absolute,
|
||||
final boolean showOnlyWhenRequested,
|
||||
@NotNull final List<String> suggestions) {
|
||||
myAbsolute = absolute;
|
||||
myShowOnlyWhenRequested = showOnlyWhenRequested;
|
||||
mySuggestions.addAll(suggestions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return suggestions
|
||||
*/
|
||||
@NotNull
|
||||
List<String> getSuggestions() {
|
||||
return Collections.unmodifiableList(mySuggestions);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <h1>Optparse-based commandline interface presenter</h1>
|
||||
* <p>
|
||||
* Command-line like interface presenter that uses conception of command and its arguments.
|
||||
* See {@link com.jetbrains.python.commandInterface.commandsWithArgs.CommandInterfacePresenterCommandBased}
|
||||
* and its arguments: {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument}.
|
||||
*
|
||||
* It supports <a href="https://docs.python.org/2/library/optparse.html">optparse</a> terminology, so
|
||||
* read it first and use {@link com.jetbrains.python.optParse} package
|
||||
* </p>
|
||||
* <h2>Arguments and validation</h2>
|
||||
* <p>
|
||||
* Optparse arguments are <strong>positional and unnamed</strong>.
|
||||
* Each {@link com.jetbrains.python.commandInterface.commandsWithArgs.Command command} provides
|
||||
* {@link com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsInfo arguments info}.
|
||||
* It can be used to obtain information about argument (like list of possible values) and it also used to validate argument values,
|
||||
* provided by user. In most cases we have no idea about arguments: due to optparse limitations only help test is available.
|
||||
* But sometimes we do know (like when args are documented).
|
||||
* Different strategies exist, so be sure to check {@link com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsInfo} children
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @see com.jetbrains.python.optParse
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandsWithArgs;
|
||||
@@ -19,7 +19,7 @@
|
||||
* It has several abilities, including (but not limited):
|
||||
* <ol>
|
||||
* <li>Suggestion box</li>
|
||||
* <li>Error marking</li>
|
||||
* <li>Error emphasising</li>
|
||||
* <li>Popups</li>
|
||||
* <li>AutoCompletion</li>
|
||||
* </ol>
|
||||
@@ -31,9 +31,15 @@
|
||||
*
|
||||
* <p>
|
||||
* There is also swing-based view implementation in {@link com.jetbrains.python.commandInterface.swingView}
|
||||
* and presenter implementation based on idea of commands with arguments. See {@link com.jetbrains.python.commandInterface.commandsWithArgs}
|
||||
* and presenter implementation based on idea of commands with arguments. See {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter}
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Presenter and View talk to each other only in term of text and chars. Presenter knows nothing about pixels and should never
|
||||
* assume view have certain pixel size.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
|
||||
+195
-86
@@ -17,16 +17,18 @@ package com.jetbrains.python.commandInterface.swingView;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorWindow;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.*;
|
||||
import com.intellij.openapi.ui.popup.Balloon.Position;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.Range;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfacePresenter;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import com.jetbrains.python.optParse.WordWithPosition;
|
||||
import com.jetbrains.python.suggestionList.SuggestionList;
|
||||
import com.jetbrains.python.suggestionList.SuggestionsBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -37,19 +39,31 @@ import javax.swing.event.CaretEvent;
|
||||
import javax.swing.event.CaretListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Command-interface view implementation based on Swing
|
||||
* Command-interface view implementation based on Swing.
|
||||
* It uses balloons to display errors and infos, drop-down for suggestions and also underlines errors
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements CommandInterfaceView, DocumentListener, CaretListener {
|
||||
private static final JBColor ERROR_COLOR = JBColor.RED;
|
||||
|
||||
/**
|
||||
* We need to track balloons, so we have field with callback
|
||||
*/
|
||||
@NotNull
|
||||
private final BalloonManager myBalloonManager = new BalloonManager();
|
||||
/**
|
||||
* Pop-up we displayed in
|
||||
*/
|
||||
@@ -68,27 +82,38 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
* Lower (sub) label
|
||||
*/
|
||||
private JLabel mySubLabel;
|
||||
/**
|
||||
* "Suggestion area". Suggestion status is displayed when caret meets this area
|
||||
*/
|
||||
private final List<Range<Integer>> myPlacesWhereSuggestionsAvailable = new ArrayList<Range<Integer>>();
|
||||
@NotNull
|
||||
private final CommandInterfacePresenter myPresenter;
|
||||
/**
|
||||
* List to display suggestions
|
||||
*/
|
||||
@NotNull
|
||||
private final SuggestionList myList;
|
||||
private final SuggestionList mySuggestionList;
|
||||
/**
|
||||
* Displayed when there is no text
|
||||
*/
|
||||
@Nullable
|
||||
private final String myPlaceHolderText;
|
||||
|
||||
/**
|
||||
* Flag that indicates we are in "test forced" mode: current text set by presenter, not by user
|
||||
* Information balloons that should be displayed when caret meets their boundaries.
|
||||
*/
|
||||
private boolean myInForcedTextMode;
|
||||
// TODO: Doc
|
||||
@NotNull
|
||||
private final List<WordWithPosition> myBalloons = new ArrayList<WordWithPosition>();
|
||||
@NotNull
|
||||
private final List<WordWithPosition> myInfoBalloons = new ArrayList<WordWithPosition>();
|
||||
/**
|
||||
* Error balloons that should be displayed when caret meets their boundaries.
|
||||
* Errors are always underlined, but balloons are displayed only if caret meets error
|
||||
*/
|
||||
private final List<WordWithPosition> myErrorBalloons = new ArrayList<WordWithPosition>();
|
||||
/**
|
||||
* Default subtext to display when caret is out of {@link #myPlacesWhereSuggestionsAvailable "suggestion" area}
|
||||
*/
|
||||
@Nullable
|
||||
private String myDefaultSubText;
|
||||
|
||||
/**
|
||||
* @param presenter our presenter
|
||||
@@ -112,18 +137,11 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
myMainTextField.setFocusable(true);
|
||||
|
||||
|
||||
final EditorWindow window = FileEditorManagerEx.getInstanceEx(project).getCurrentWindow();
|
||||
final int windowSize;
|
||||
if (window != null) {
|
||||
windowSize = window.getSize().width;
|
||||
}
|
||||
else {
|
||||
windowSize = 0; // Windows size is unknown
|
||||
}
|
||||
final int windowWidth = FileEditorManagerEx.getInstanceEx(project).getComponent().getRootPane().getWidth() - 10; // Little gap
|
||||
|
||||
myMainTextField
|
||||
.setPreferredWidthInPx(windowSize);
|
||||
myList = new SuggestionList(new MySuggestionListListener());
|
||||
.setPreferredWidthInPx(windowWidth);
|
||||
mySuggestionList = new SuggestionList(new MySuggestionListListener());
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +163,12 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
}
|
||||
});
|
||||
myMainTextField.setFocusTraversalKeysEnabled(false);
|
||||
myMainTextField.addKeyListener(new MyKeyListener());
|
||||
myMainTextField.addKeyListener(new MyKeyListener()); // Up/down arrows are not handles with actions
|
||||
|
||||
// Register all available actions
|
||||
for (final KeyStrokeInfo strokeInfo : KeyStrokeInfo.values()) {
|
||||
strokeInfo.register(myPresenter, mySuggestionList, myMainTextField);
|
||||
}
|
||||
myMainPopUp.showInFocusCenter();
|
||||
}
|
||||
|
||||
@@ -154,32 +177,18 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
public void displaySuggestions(@NotNull final SuggestionsBuilder suggestions, final boolean absolute, @Nullable final String toSelect) {
|
||||
|
||||
int left = 0;
|
||||
// Display text right after line ends if not in "absolute" mode
|
||||
// Display text right after caret
|
||||
if (!absolute) {
|
||||
left = myMainTextField.getTextEndPosition();
|
||||
left = myMainTextField.getTextCaretPositionInPx();
|
||||
}
|
||||
myList.showSuggestions(suggestions, new RelativePoint(myPanel, new Point(left, myPanel.getHeight())), toSelect);
|
||||
mySuggestionList.showSuggestions(suggestions, new RelativePoint(myPanel, new Point(left, myPanel.getHeight())), toSelect);
|
||||
configureAppropriateStatus();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClosed(final LightweightWindowEvent event) {
|
||||
super.onClosed(event);
|
||||
myList.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void showErrors(@NotNull final List<WordWithPosition> errors, @Nullable final SpecialErrorPlace specialErrorPlace) {
|
||||
for (final WordWithPosition error : errors) {
|
||||
myMainTextField.underlineText(ERROR_COLOR, error.getFrom(), error.getTo());
|
||||
}
|
||||
if (specialErrorPlace != null) {
|
||||
myMainTextField.underlineText(specialErrorPlace, ERROR_COLOR);
|
||||
}
|
||||
synchronized (myErrorBalloons) {
|
||||
myErrorBalloons.clear();
|
||||
myErrorBalloons.addAll(errors);
|
||||
}
|
||||
mySuggestionList.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +197,7 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
processDocumentChange();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removeUpdate(final DocumentEvent e) {
|
||||
processDocumentChange();
|
||||
@@ -195,7 +205,7 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
|
||||
private void processDocumentChange() {
|
||||
myMainTextField.hideUnderline();
|
||||
myPresenter.textChanged(myInForcedTextMode);
|
||||
myPresenter.textChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,75 +214,155 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceText(@NotNull final String newText) {
|
||||
myInForcedTextMode = true;
|
||||
myMainTextField.setText(newText);
|
||||
myInForcedTextMode = false;
|
||||
public void removeSuggestions() {
|
||||
mySuggestionList.close();
|
||||
configureAppropriateStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSuggestions() {
|
||||
myList.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void caretUpdate(final CaretEvent e) {
|
||||
|
||||
// TODO: Stupid copy/paste, fix by method extract
|
||||
|
||||
// When caret moved, we need to check if balloon has to be displayed
|
||||
synchronized (myErrorBalloons) {
|
||||
showBaloons(myErrorBalloons, Position.below, MessageType.ERROR);
|
||||
displayBalloonsIfRequired();
|
||||
configureAppropriateStatus();
|
||||
}
|
||||
|
||||
private void configureAppropriateStatus() {
|
||||
if (!mySuggestionList.isClosed()) {
|
||||
// Tell user she may use TAB to complete
|
||||
mySubLabel.setText(PyBundle.message("commandLine.subText.key.complete", KeyStrokeInfo.COMPLETION.getText()));
|
||||
return;
|
||||
}
|
||||
synchronized (myBalloons) {
|
||||
showBaloons(myBalloons, Position.above, MessageType.INFO);
|
||||
|
||||
// If we are in "suggestion available" place -- tell it
|
||||
for (final Range<Integer> range : myPlacesWhereSuggestionsAvailable) {
|
||||
final boolean specialCaseAfterLastChar = isAfterLastCharRange(range) && getCaretPosition() == myMainTextField.getText().length();
|
||||
if (range.isWithin(getCaretPosition()) || specialCaseAfterLastChar) {
|
||||
mySubLabel.setText(PyBundle.message("commandLine.subText.key.suggestions", KeyStrokeInfo.SUGGESTION.getText()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We may simply tell user she may execute command
|
||||
if (myDefaultSubText != null) {
|
||||
mySubLabel.setText(PyBundle.message("commandLine.subText.key.executeCommand", KeyStrokeInfo.EXECUTION.getText(), myDefaultSubText));
|
||||
}
|
||||
else {
|
||||
mySubLabel.setText(PyBundle.message("commandLine.subText.key.executeUnknown", KeyStrokeInfo.EXECUTION.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Doc
|
||||
private void showBaloons(@NotNull final List<WordWithPosition> balloons,
|
||||
@NotNull final Position popUpPosition,
|
||||
@NotNull final MessageType messageType) {
|
||||
|
||||
private void displayBalloonsIfRequired() {
|
||||
synchronized (myErrorBalloons) {
|
||||
if (mySuggestionList.isClosed()) { // No need to display error popups when suggestion list is displayed. It intersects.
|
||||
showBalloons(myErrorBalloons, Position.below, MessageType.ERROR);
|
||||
}
|
||||
}
|
||||
synchronized (myInfoBalloons) {
|
||||
showBalloons(myInfoBalloons, Position.above, MessageType.INFO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays some balloons
|
||||
*
|
||||
* @param balloons balloons to display
|
||||
* @param popUpPosition where ti display them. Only {@link Position#above} and {@link Position#below} are supported!
|
||||
* @param messageType may be {@link MessageType#ERROR} or {@link MessageType#INFO} for example
|
||||
*/
|
||||
private void showBalloons(@NotNull final List<WordWithPosition> balloons,
|
||||
@NotNull final Position popUpPosition,
|
||||
@NotNull final MessageType messageType) {
|
||||
Preconditions.checkArgument(popUpPosition == Position.above || popUpPosition == Position.below, "Only above or below is supported");
|
||||
for (final WordWithPosition balloon : balloons) {
|
||||
if (balloon.getText().isEmpty()) {
|
||||
continue; // Can't be displayed if empty
|
||||
}
|
||||
final int caretPosition = myMainTextField.getCaretPosition();
|
||||
if (caretPosition >= balloon.getFrom() && caretPosition <= balloon.getTo()) {
|
||||
if ((caretPosition >= balloon.getFrom() && caretPosition <= balloon.getTo())) {
|
||||
final int top = (popUpPosition == Position.above ? 0 : myMainTextField.getHeight() * 2); // Display below a little bit lower
|
||||
final RelativePoint point = new RelativePoint(myMainTextField, new Point(myMainTextField.getTextCursorPosition(), top));
|
||||
final RelativePoint point = new RelativePoint(myMainTextField, new Point(myMainTextField.getTextCaretPositionInPx(), top));
|
||||
final Balloon balloonToShow =
|
||||
JBPopupFactory.getInstance().createBalloonBuilder(new JLabel(balloon.getText())).setFillColor(messageType.getPopupBackground())
|
||||
.createBalloon();
|
||||
balloonToShow.setAnimationEnabled(false);
|
||||
myBalloonManager.registerBalloon(balloonToShow);
|
||||
balloonToShow.show(point, popUpPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isCaretOnWord() {
|
||||
final int caretPosition = myMainTextField.getCaretPosition();
|
||||
if (caretPosition == 0) {
|
||||
return false; // At the beginning of the line
|
||||
public final void setInfoAndErrors(@NotNull final Collection<WordWithPosition> infoBalloons,
|
||||
@NotNull final Collection<WordWithPosition> errors) {
|
||||
synchronized (myInfoBalloons) {
|
||||
myInfoBalloons.clear();
|
||||
myInfoBalloons.addAll(infoBalloons);
|
||||
}
|
||||
return !Character.isWhitespace(myMainTextField.getText().toCharArray()[caretPosition - 1]);
|
||||
synchronized (myErrorBalloons) {
|
||||
myErrorBalloons.clear();
|
||||
myErrorBalloons.addAll(errors);
|
||||
}
|
||||
for (final WordWithPosition error : errors) {
|
||||
if (isAfterLastCharRange(error)) {
|
||||
// In "special" case we use last char
|
||||
myMainTextField.underlineText(ERROR_COLOR, myMainTextField.getText().length(), myMainTextField.getText().length() + 1);
|
||||
}
|
||||
else {
|
||||
myMainTextField.underlineText(ERROR_COLOR, error.getFrom(), error.getTo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if some range is <strong>special case</strong> {@link #AFTER_LAST_CHARACTER_RANGE}.
|
||||
*
|
||||
* @param range range to check
|
||||
* @return true if special case
|
||||
*/
|
||||
private static boolean isAfterLastCharRange(@NotNull final Range<Integer> range) {
|
||||
return AFTER_LAST_CHARACTER_RANGE.getFrom().equals(range.getFrom()) && AFTER_LAST_CHARACTER_RANGE.getTo().equals(range.getTo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBalloons(@NotNull final Collection<WordWithPosition> balloons) {
|
||||
synchronized (myBalloons) {
|
||||
myBalloons.clear();
|
||||
myBalloons.addAll(balloons);
|
||||
public final void insertTextAfterCaret(@NotNull final String text) {
|
||||
try {
|
||||
myMainTextField.getDocument().insertString(myMainTextField.getCaretPosition(), text, null);
|
||||
}
|
||||
catch (final BadLocationException e) {
|
||||
// TODO: Display error somehow!
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubText(@NotNull final String subText) {
|
||||
mySubLabel.setText(subText);
|
||||
public final void replaceText(final int from, final int to, @NotNull final String newText) {
|
||||
myMainTextField.select(from, to);
|
||||
myMainTextField.replaceSelection(newText);
|
||||
myBalloonManager.closeAllBalloons();
|
||||
myPresenter.textChanged();
|
||||
displayBalloonsIfRequired(); // This crunch but we need to recalculate balloons in this case (position is changed!)
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getCaretPosition() {
|
||||
return myMainTextField.getCaretPosition();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void configureSubTexts(@Nullable final String defaultSubText,
|
||||
@NotNull final List<Range<Integer>> suggestionAvailablePlaces) {
|
||||
synchronized (myPlacesWhereSuggestionsAvailable) {
|
||||
myPlacesWhereSuggestionsAvailable.clear();
|
||||
myPlacesWhereSuggestionsAvailable.addAll(suggestionAvailablePlaces);
|
||||
myDefaultSubText = defaultSubText;
|
||||
}
|
||||
configureAppropriateStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts on keys, pressed by user
|
||||
@@ -281,21 +371,13 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
@Override
|
||||
public void keyPressed(final KeyEvent e) {
|
||||
super.keyPressed(e);
|
||||
|
||||
final int keyCode = e.getKeyCode();
|
||||
if (keyCode == KeyEvent.VK_UP) {
|
||||
myList.moveSelection(true);
|
||||
mySuggestionList.moveSelection(true);
|
||||
}
|
||||
else if (keyCode == KeyEvent.VK_DOWN) {
|
||||
myList.moveSelection(false);
|
||||
}
|
||||
else if (keyCode == KeyEvent.VK_ENTER) {
|
||||
myPresenter.executionRequested(myList.getValue());
|
||||
}
|
||||
else if (keyCode == KeyEvent.VK_TAB) {
|
||||
myPresenter.completionRequested(myList.getValue());
|
||||
}
|
||||
else if ((keyCode == KeyEvent.VK_SPACE) && (e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK)) {
|
||||
myPresenter.suggestionRequested();
|
||||
mySuggestionList.moveSelection(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,10 +388,6 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
return myMainTextField.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreferredWidthInChars(final int widthInChars) {
|
||||
myMainTextField.setPreferredWidthInChars(widthInChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for suggestion list
|
||||
@@ -322,4 +400,35 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com
|
||||
removeSuggestions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps tracks for baloons to close all of them in case of text inserion
|
||||
*/
|
||||
private static final class BalloonManager extends JBPopupAdapter {
|
||||
@NotNull
|
||||
private final Set<Balloon> myCurrentBaloons = new HashSet<Balloon>();
|
||||
|
||||
void registerBalloon(final Balloon balloon) {
|
||||
synchronized (myCurrentBaloons) {
|
||||
myCurrentBaloons.add(balloon);
|
||||
balloon.addListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(final LightweightWindowEvent event) {
|
||||
synchronized (myCurrentBaloons) {
|
||||
myCurrentBaloons.remove(event.asBalloon());
|
||||
}
|
||||
super.onClosed(event);
|
||||
}
|
||||
|
||||
void closeAllBalloons() {
|
||||
synchronized (myCurrentBaloons) {
|
||||
for (final Balloon balloon : myCurrentBaloons) {
|
||||
balloon.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.swingView;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
/**
|
||||
* "Complete current command or argument" action
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class CompletionKeyStrokeAction extends KeyStrokeAction {
|
||||
CompletionKeyStrokeAction() {
|
||||
super(KeyStrokeInfo.COMPLETION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
myPresenter.completionRequested(mySuggestionList.getValue());
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.swingView;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
/**
|
||||
* "Execute command" action
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class ExecutionKeyStrokeAction extends KeyStrokeAction {
|
||||
ExecutionKeyStrokeAction() {
|
||||
super(KeyStrokeInfo.EXECUTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
myPresenter.executionRequested();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.swingView;
|
||||
|
||||
import com.jetbrains.python.commandInterface.CommandInterfacePresenter;
|
||||
import com.jetbrains.python.suggestionList.SuggestionList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.text.TextAction;
|
||||
|
||||
/**
|
||||
* Action that should be taken for certain {@link javax.swing.KeyStroke} (wrapped in {@link com.jetbrains.python.commandInterface.swingView.KeyStrokeInfo}).
|
||||
* You need to call {@link #configure(com.jetbrains.python.commandInterface.CommandInterfacePresenter, com.jetbrains.python.suggestionList.SuggestionList)}
|
||||
* to enable one.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
@SuppressWarnings({"InstanceVariableMayNotBeInitialized", "NonSerializableFieldInSerializableClass"}) // Will never serialize
|
||||
abstract class KeyStrokeAction extends TextAction {
|
||||
@NotNull
|
||||
private final String myName;
|
||||
@NotNull
|
||||
private final KeyStrokeInfo myStroke;
|
||||
|
||||
protected CommandInterfacePresenter myPresenter;
|
||||
protected SuggestionList mySuggestionList;
|
||||
|
||||
/**
|
||||
* @param stroke key stroke to bind this info to
|
||||
*/
|
||||
KeyStrokeAction(@NotNull final KeyStrokeInfo stroke) {
|
||||
super(stroke.name());
|
||||
myName = stroke.name();
|
||||
myStroke = stroke;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures action.
|
||||
*
|
||||
* @param presenter presenter to be used for call back.
|
||||
* @param suggestionList list of suggestions to be used for call back
|
||||
* @return name of this action to add to {@link javax.swing.InputMap}
|
||||
*/
|
||||
@NotNull
|
||||
final String configure(@NotNull final CommandInterfacePresenter presenter, @NotNull final SuggestionList suggestionList) {
|
||||
myPresenter = presenter;
|
||||
mySuggestionList = suggestionList;
|
||||
return myName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return stroke bound to this action
|
||||
*/
|
||||
@NotNull
|
||||
final KeyStrokeInfo getStroke() {
|
||||
return myStroke;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.swingView;
|
||||
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfacePresenter;
|
||||
import com.jetbrains.python.suggestionList.SuggestionList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
/**
|
||||
* Key strokes to be used with view.
|
||||
* Strokes paired with action. You need to register each action via {@link javax.swing.InputMap}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
enum KeyStrokeInfo {
|
||||
/**
|
||||
* "Execute command" keystroke
|
||||
*/
|
||||
EXECUTION(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)),
|
||||
/**
|
||||
* "Complete current command or argument" keystroke
|
||||
*/
|
||||
COMPLETION(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)),
|
||||
/**
|
||||
* "Display suggestions" keystroke.
|
||||
*/
|
||||
SUGGESTION(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK)),;
|
||||
|
||||
/**
|
||||
* List of actions. Each action should be bound to some {@link com.jetbrains.python.commandInterface.swingView.KeyStrokeInfo}
|
||||
*/
|
||||
@NotNull
|
||||
private static final KeyStrokeAction[] ACTIONS = {
|
||||
new CompletionKeyStrokeAction(),
|
||||
new ExecutionKeyStrokeAction(),
|
||||
new SuggestionKeyStrokeAction()};
|
||||
|
||||
private final KeyStroke myStroke;
|
||||
|
||||
KeyStrokeInfo(@NotNull final KeyStroke stroke) {
|
||||
myStroke = stroke;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers action and binds it appropriate stroke. Call if for all instances to make all actions available.
|
||||
*
|
||||
* @param presenter presenter to be used as call back
|
||||
* @param suggestionList suggestion list to be used as call back
|
||||
* @param source Component with {@link javax.swing.InputMap} and {@link javax.swing.ActionMap} (swing view itself)
|
||||
*/
|
||||
void register(@NotNull final CommandInterfacePresenter presenter,
|
||||
@NotNull final SuggestionList suggestionList,
|
||||
@NotNull final JComponent source) {
|
||||
final KeyStrokeAction action = getAction();
|
||||
final String strokeName = action.configure(presenter, suggestionList);
|
||||
source.getInputMap().put(myStroke, strokeName);
|
||||
source.getActionMap().put(strokeName, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Human-readable name of this action (to display it to user)
|
||||
*/
|
||||
@NotNull
|
||||
String getText() {
|
||||
return KeymapUtil.getKeystrokeText(myStroke);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return action paired with stroke
|
||||
*/
|
||||
@NotNull
|
||||
private KeyStrokeAction getAction() {
|
||||
for (final KeyStrokeAction action : ACTIONS) {
|
||||
if (action.getStroke() == this) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Failed to find action for " + name());
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,9 @@ package com.jetbrains.python.commandInterface.swingView;
|
||||
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.Range;
|
||||
import com.intellij.util.ui.StatusText;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -47,11 +43,6 @@ public class SmartTextField extends JTextField {
|
||||
@NotNull
|
||||
private final Collection<UnderlineInfo> myUnderlineInfo = new ArrayList<UnderlineInfo>();
|
||||
private int myPreferredWidth;
|
||||
/**
|
||||
* (color, special_place) tuple to underline special place, or null if no underline required
|
||||
*/
|
||||
@Nullable
|
||||
private Pair<Color, SpecialErrorPlace> mySpecialUnderlinePlace;
|
||||
|
||||
public SmartTextField() {
|
||||
setFont(EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.CONSOLE_PLAIN));
|
||||
@@ -66,15 +57,9 @@ public class SmartTextField extends JTextField {
|
||||
synchronized (myUnderlineInfo) {
|
||||
for (final UnderlineInfo underlineInfo : myUnderlineInfo) {
|
||||
g.setColor(underlineInfo.myColor);
|
||||
// To prevent too long underlines: last char should really be last
|
||||
underline(g, underlineInfo.getFrom(), underlineInfo.getTo());
|
||||
}
|
||||
if (mySpecialUnderlinePlace != null) {
|
||||
final SpecialErrorPlace place = mySpecialUnderlinePlace.second;
|
||||
g.setColor(mySpecialUnderlinePlace.first);
|
||||
final int endPosition = getTextEndPosition();
|
||||
final int from = (place == SpecialErrorPlace.WHOLE_TEXT ? 0 : endPosition - getColumnWidth());
|
||||
underline(g, from, endPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,17 +75,11 @@ public class SmartTextField extends JTextField {
|
||||
g.drawLine(from + getColumnWidth(), verticalPosition, to + getColumnWidth(), verticalPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return place (in px) where entered text ends.
|
||||
*/
|
||||
int getTextEndPosition() {
|
||||
return (getText().length() + 1) * getColumnWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return place (in px) where caret.
|
||||
*/
|
||||
int getTextCursorPosition() {
|
||||
int getTextCaretPositionInPx() {
|
||||
return (getCaretPosition() + 1) * getColumnWidth();
|
||||
}
|
||||
|
||||
@@ -139,19 +118,9 @@ public class SmartTextField extends JTextField {
|
||||
void hideUnderline() {
|
||||
synchronized (myUnderlineInfo) {
|
||||
myUnderlineInfo.clear();
|
||||
mySpecialUnderlinePlace = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets appropriate width in chars
|
||||
*
|
||||
* @param widthInChars num of chars
|
||||
*/
|
||||
void setPreferredWidthInChars(final int widthInChars) {
|
||||
setColumns(widthInChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets appropriate width in pixels
|
||||
*
|
||||
@@ -161,18 +130,6 @@ public class SmartTextField extends JTextField {
|
||||
myPreferredWidth = width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display underline in special place
|
||||
*
|
||||
* @param color color to underline
|
||||
* @param specialUnderlinePlace special place to underline
|
||||
*/
|
||||
void underlineText(@NotNull final SpecialErrorPlace specialUnderlinePlace,
|
||||
@NotNull final Color color) {
|
||||
synchronized (myUnderlineInfo) {
|
||||
mySpecialUnderlinePlace = Pair.create(color, specialUnderlinePlace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to display placeholder
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandInterface.swingView;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
/**
|
||||
* "Suggestion request" action
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class SuggestionKeyStrokeAction extends KeyStrokeAction {
|
||||
SuggestionKeyStrokeAction() {
|
||||
super(KeyStrokeInfo.SUGGESTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
myPresenter.suggestionRequested();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command line parse result.
|
||||
* It consists of command itself and its parts.
|
||||
* Each part may be {@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT argument} or
|
||||
* {@link com.jetbrains.python.commandLineParser.CommandLinePartType#OPTION option} or something else.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineParseResult {
|
||||
@NotNull
|
||||
private final List<Pair<CommandLinePartType, WordWithPosition>> myParts = new ArrayList<Pair<CommandLinePartType, WordWithPosition>>();
|
||||
@NotNull
|
||||
private final WordWithPosition myCommand;
|
||||
|
||||
CommandLineParseResult(
|
||||
@NotNull final WordWithPosition command,
|
||||
@NotNull final Collection<Pair<CommandLinePartType, WordWithPosition>> parts) {
|
||||
myCommand = command;
|
||||
myParts.addAll(parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command (i.e. "startapp" in "startapp my_app")
|
||||
*/
|
||||
@NotNull
|
||||
public WordWithPosition getCommand() {
|
||||
return myCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of parts in format [part_type, value].
|
||||
* For example (rm my_folder): [{@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT argument}, my_folder]
|
||||
*/
|
||||
@NotNull
|
||||
public List<Pair<CommandLinePartType, WordWithPosition>> getParts() {
|
||||
return Collections.unmodifiableList(myParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all command line parts with out of part information (just words and positions).
|
||||
* Note tha command itself is not part, only args and options are
|
||||
* @see #getParts()
|
||||
*/
|
||||
@NotNull
|
||||
public Collection<WordWithPosition> getPartsNoType() {
|
||||
final Collection<WordWithPosition> result = new ArrayList<WordWithPosition>();
|
||||
for (final Pair<CommandLinePartType, WordWithPosition> part : myParts) {
|
||||
result.add(part.second);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Engine to parse command line. It understands how options and arguments are coded in certain commandline.
|
||||
* It supportd {@link com.jetbrains.python.WordWithPosition} telling you exactly with part of
|
||||
* command line is command or argument. That helps you to underline or emphisize some parts.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface CommandLineParser {
|
||||
/**
|
||||
* @param commandLineParts command line splitted into words.
|
||||
* @return command line information
|
||||
* @throws MalformedCommandLineException in case of bad commandline
|
||||
*/
|
||||
@NotNull
|
||||
CommandLineParseResult parse(@NotNull List<WordWithPosition> commandLineParts) throws MalformedCommandLineException;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2000-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 com.jetbrains.python.commandLineParser;
|
||||
|
||||
/**
|
||||
* Types of command line parts.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public enum CommandLinePartType {
|
||||
/**
|
||||
* Argument (or positional, or unnamed argument) something that has only value. Like "my_folder" in "rm my_folder"
|
||||
*/
|
||||
ARGUMENT,
|
||||
/**
|
||||
* Option is named but optional parameter. Like "-l" in "ls -l".
|
||||
*/
|
||||
OPTION,
|
||||
/**
|
||||
* Some part of command line that {@link com.jetbrains.python.commandLineParser.CommandLineParser} does not understand
|
||||
*/
|
||||
UNKNOWN
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.optParse;
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
+17
-51
@@ -13,11 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.optParse;
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: Support options and their arguments
|
||||
|
||||
@@ -36,23 +41,17 @@ import java.util.*;
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class ParsedCommandLine {
|
||||
public final class OptParseCommandLineParser implements CommandLineParser {
|
||||
@NotNull
|
||||
private final WordWithPosition myCommand;
|
||||
@NotNull
|
||||
private final List<WordWithPosition> myArguments = new ArrayList<WordWithPosition>();
|
||||
|
||||
/**
|
||||
* @param commandLine command line to parse
|
||||
* @throws MalformedCommandLineException if malformed commandline can't be parsed
|
||||
*/
|
||||
public ParsedCommandLine(@NotNull final String commandLine) throws MalformedCommandLineException {
|
||||
final Deque<WordWithPosition> parts = new LinkedList<WordWithPosition>(WordWithPosition.splitText(commandLine));
|
||||
@Override
|
||||
public CommandLineParseResult parse(@NotNull final List<WordWithPosition> commandLineParts) throws MalformedCommandLineException {
|
||||
final Deque<WordWithPosition> parts = new ArrayDeque<WordWithPosition>(commandLineParts);
|
||||
if (parts.isEmpty()) {
|
||||
throw new MalformedCommandLineException("No command provided");
|
||||
}
|
||||
myCommand = parts.pop();
|
||||
if (myCommand.getText().startsWith("-")) {
|
||||
final WordWithPosition command = parts.pop();
|
||||
final List<Pair<CommandLinePartType, WordWithPosition>> resultParts = new ArrayList<Pair<CommandLinePartType, WordWithPosition>>();
|
||||
if (command.getText().startsWith("-")) {
|
||||
throw new MalformedCommandLineException("Command can't start with option prefix");
|
||||
}
|
||||
|
||||
@@ -60,46 +59,13 @@ public final class ParsedCommandLine {
|
||||
for (final WordWithPosition part : parts) {
|
||||
if (part.getText().startsWith("-")) {
|
||||
// This is option!
|
||||
resultParts.add(Pair.create(CommandLinePartType.OPTION, part));
|
||||
}
|
||||
else {
|
||||
// TODO: Check optopn argument!
|
||||
myArguments.add(part);
|
||||
resultParts.add(Pair.create(CommandLinePartType.ARGUMENT, part));
|
||||
}
|
||||
}
|
||||
return new CommandLineParseResult(command, resultParts);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return command (i.e. "startapp" in "startapp my_app")
|
||||
*/
|
||||
@NotNull
|
||||
public WordWithPosition getCommand() {
|
||||
return myCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all arguments (not options or option arguments!)
|
||||
*/
|
||||
@NotNull
|
||||
public List<WordWithPosition> getArguments() {
|
||||
return Collections.unmodifiableList(myArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all parts for command line as simple words
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getAsWords() {
|
||||
final List<String> result = new ArrayList<String>();
|
||||
|
||||
result.add(myCommand.getText());
|
||||
|
||||
for (final WordWithPosition argument : myArguments) {
|
||||
result.add(argument.getText());
|
||||
}
|
||||
// TODO: Add options as well
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Add options, arguments and option arguments
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2000-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Engine to parse command-line.
|
||||
* Command line consists of command itself, {@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT arguments}
|
||||
* and {@link com.jetbrains.python.commandLineParser.CommandLinePartType#OPTION options}.
|
||||
* Use need to split command line into {@link com.jetbrains.python.WordWithPosition chunks}, pass them to
|
||||
* {@link com.jetbrains.python.commandLineParser.CommandLineParser parser} and obtain {@link com.jetbrains.python.commandLineParser.CommandLineParseResult}.
|
||||
*
|
||||
* Not like any other parsers, this package supports {@link com.jetbrains.python.WordWithPosition} telling you exactly with part of
|
||||
* command line is command or argument. That helps you to underline or emphisize some parts.
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An <a href="https://docs.python.org/2/library/optparse.html">optparse</a> module twin, that parses command line.
|
||||
* Unlike any other GNU/Posix parsers, it knows how to:</p>
|
||||
* <ol>
|
||||
* <li>Parse options and args with out of any knowledge about required args</li>
|
||||
* <li>Provide actual places in command line where exactly such args or opts exist./li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* Be sure to read optparse manual
|
||||
* (epecially <a href="https://docs.python.org/2/library/optparse.html#terminology">terminology</a>) part.
|
||||
* </p>
|
||||
* <p>Package entry point is {@link com.jetbrains.python.optParse.ParsedCommandLine}</p>
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.optParse;
|
||||
@@ -166,7 +166,14 @@ public class SuggestionList {
|
||||
return null; // Nothing is selected if list is invisible
|
||||
}
|
||||
final Object value = myList.getSelectedValue();
|
||||
return ((value == null) ? "" : getElement(value).mySuggestion.getText());
|
||||
return ((value == null) ? null : getElement(value).mySuggestion.getText());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if no suggestion list is displayed now.
|
||||
*/
|
||||
public final synchronized boolean isClosed() {
|
||||
return myListPopUp == null || myListPopUp.isDisposed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user