mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
PSI based on Grammar-Kit created for command line language in order to implement PY-11855
* GrammarKit instead of custom parsing * ToolWindow instead of popup * Inspection and help added
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.impl.*;
|
||||
|
||||
public interface CommandLineElementTypes {
|
||||
|
||||
IElementType ARGUMENT = new CommandLineElementType("ARGUMENT");
|
||||
IElementType COMMAND = new CommandLineElementType("COMMAND");
|
||||
IElementType OPTION = new CommandLineElementType("OPTION");
|
||||
|
||||
IElementType EQ = new IElementType("=", null);
|
||||
IElementType LITERAL_STARTS_FROM_DIGIT = new IElementType("LITERAL_STARTS_FROM_DIGIT", null);
|
||||
IElementType LITERAL_STARTS_FROM_LETTER = new IElementType("LITERAL_STARTS_FROM_LETTER", null);
|
||||
IElementType LONG_OPTION_NAME_TOKEN = new IElementType("LONG_OPTION_NAME_TOKEN", null);
|
||||
IElementType SHORT_OPTION_NAME_TOKEN = new IElementType("SHORT_OPTION_NAME_TOKEN", null);
|
||||
|
||||
class Factory {
|
||||
public static PsiElement createElement(ASTNode node) {
|
||||
IElementType type = node.getElementType();
|
||||
if (type == ARGUMENT) {
|
||||
return new CommandLineArgumentImpl(node);
|
||||
}
|
||||
else if (type == COMMAND) {
|
||||
return new CommandLineCommandImpl(node);
|
||||
}
|
||||
else if (type == OPTION) {
|
||||
return new CommandLineOptionImpl(node);
|
||||
}
|
||||
throw new AssertionError("Unknown element type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.lang.PsiBuilder;
|
||||
import com.intellij.lang.PsiBuilder.Marker;
|
||||
import static com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes.*;
|
||||
import static com.jetbrains.commandInterface.gnuCommandLine.CommandLineParserUtil.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.lang.PsiParser;
|
||||
|
||||
@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"})
|
||||
public class CommandLineParser implements PsiParser {
|
||||
|
||||
public ASTNode parse(IElementType t, PsiBuilder b) {
|
||||
parseLight(t, b);
|
||||
return b.getTreeBuilt();
|
||||
}
|
||||
|
||||
public void parseLight(IElementType t, PsiBuilder b) {
|
||||
boolean r;
|
||||
b = adapt_builder_(t, b, this, null);
|
||||
Marker m = enter_section_(b, 0, _COLLAPSE_, null);
|
||||
if (t == ARGUMENT) {
|
||||
r = argument(b, 0);
|
||||
}
|
||||
else if (t == COMMAND) {
|
||||
r = command(b, 0);
|
||||
}
|
||||
else if (t == OPTION) {
|
||||
r = option(b, 0);
|
||||
}
|
||||
else {
|
||||
r = parse_root_(t, b, 0);
|
||||
}
|
||||
exit_section_(b, 0, m, t, r, true, TRUE_CONDITION);
|
||||
}
|
||||
|
||||
protected boolean parse_root_(IElementType t, PsiBuilder b, int l) {
|
||||
return root(b, l + 1);
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// LITERAL_STARTS_FROM_LETTER | LITERAL_STARTS_FROM_DIGIT
|
||||
public static boolean argument(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "argument")) return false;
|
||||
if (!nextTokenIs(b, "<argument>", LITERAL_STARTS_FROM_DIGIT, LITERAL_STARTS_FROM_LETTER)) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b, l, _NONE_, "<argument>");
|
||||
r = consumeToken(b, LITERAL_STARTS_FROM_LETTER);
|
||||
if (!r) r = consumeToken(b, LITERAL_STARTS_FROM_DIGIT);
|
||||
exit_section_(b, l, m, ARGUMENT, r, false, null);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// LITERAL_STARTS_FROM_LETTER
|
||||
public static boolean command(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "command")) return false;
|
||||
if (!nextTokenIs(b, LITERAL_STARTS_FROM_LETTER)) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b);
|
||||
r = consumeToken(b, LITERAL_STARTS_FROM_LETTER);
|
||||
exit_section_(b, m, COMMAND, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// LONG_OPTION_NAME_TOKEN
|
||||
static boolean long_option_name(PsiBuilder b, int l) {
|
||||
return consumeToken(b, LONG_OPTION_NAME_TOKEN);
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// short_option_name <<bound_argument>> ? | long_option_name <<bound_argument>> ?
|
||||
public static boolean option(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "option")) return false;
|
||||
if (!nextTokenIs(b, "<option>", LONG_OPTION_NAME_TOKEN, SHORT_OPTION_NAME_TOKEN)) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b, l, _NONE_, "<option>");
|
||||
r = option_0(b, l + 1);
|
||||
if (!r) r = option_1(b, l + 1);
|
||||
exit_section_(b, l, m, OPTION, r, false, null);
|
||||
return r;
|
||||
}
|
||||
|
||||
// short_option_name <<bound_argument>> ?
|
||||
private static boolean option_0(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "option_0")) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b);
|
||||
r = short_option_name(b, l + 1);
|
||||
r = r && option_0_1(b, l + 1);
|
||||
exit_section_(b, m, null, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// <<bound_argument>> ?
|
||||
private static boolean option_0_1(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "option_0_1")) return false;
|
||||
bound_argument(b, l + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// long_option_name <<bound_argument>> ?
|
||||
private static boolean option_1(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "option_1")) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b);
|
||||
r = long_option_name(b, l + 1);
|
||||
r = r && option_1_1(b, l + 1);
|
||||
exit_section_(b, m, null, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// <<bound_argument>> ?
|
||||
private static boolean option_1_1(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "option_1_1")) return false;
|
||||
bound_argument(b, l + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// command (argument | option ) * <<eof>>
|
||||
static boolean root(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "root")) return false;
|
||||
if (!nextTokenIs(b, LITERAL_STARTS_FROM_LETTER)) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b);
|
||||
r = command(b, l + 1);
|
||||
r = r && root_1(b, l + 1);
|
||||
r = r && eof(b, l + 1);
|
||||
exit_section_(b, m, null, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// (argument | option ) *
|
||||
private static boolean root_1(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "root_1")) return false;
|
||||
int c = current_position_(b);
|
||||
while (true) {
|
||||
if (!root_1_0(b, l + 1)) break;
|
||||
if (!empty_element_parsed_guard_(b, "root_1", c)) break;
|
||||
c = current_position_(b);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// argument | option
|
||||
private static boolean root_1_0(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "root_1_0")) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b);
|
||||
r = argument(b, l + 1);
|
||||
if (!r) r = option(b, l + 1);
|
||||
exit_section_(b, m, null, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// SHORT_OPTION_NAME_TOKEN
|
||||
static boolean short_option_name(PsiBuilder b, int l) {
|
||||
return consumeToken(b, SHORT_OPTION_NAME_TOKEN);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
import com.intellij.lexer.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import static com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes.*;
|
||||
|
||||
%%
|
||||
|
||||
%{
|
||||
public _CommandLineLexer() {
|
||||
this((java.io.Reader)null);
|
||||
}
|
||||
%}
|
||||
|
||||
%public
|
||||
%class _CommandLineLexer
|
||||
%implements FlexLexer
|
||||
%function advance
|
||||
%type IElementType
|
||||
%unicode
|
||||
|
||||
EOL="\r"|"\n"|"\r\n"
|
||||
LINE_WS=[\ \t\f]
|
||||
WHITE_SPACE=({LINE_WS}|{EOL})+
|
||||
|
||||
LITERAL_STARTS_FROM_LETTER=[:letter:]([a-zA-Z_0-9]|:|\\|"/"|\.)*
|
||||
LITERAL_STARTS_FROM_DIGIT=[:digit:]([a-zA-Z_0-9]|:|\\\|"/"|\.)*
|
||||
SHORT_OPTION_NAME_TOKEN=-[:letter:]
|
||||
LONG_OPTION_NAME_TOKEN=--[:letter:](-|[a-zA-Z_0-9])*
|
||||
|
||||
%%
|
||||
<YYINITIAL> {
|
||||
{WHITE_SPACE} { return com.intellij.psi.TokenType.WHITE_SPACE; }
|
||||
|
||||
"=" { return EQ; }
|
||||
|
||||
{LITERAL_STARTS_FROM_LETTER} { return LITERAL_STARTS_FROM_LETTER; }
|
||||
{LITERAL_STARTS_FROM_DIGIT} { return LITERAL_STARTS_FROM_DIGIT; }
|
||||
{SHORT_OPTION_NAME_TOKEN} { return SHORT_OPTION_NAME_TOKEN; }
|
||||
{LONG_OPTION_NAME_TOKEN} { return LONG_OPTION_NAME_TOKEN; }
|
||||
|
||||
[^] { return com.intellij.psi.TokenType.BAD_CHARACTER; }
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
/* The following code was generated by JFlex 1.4.3 on 12.03.15 21:27 */
|
||||
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
import com.intellij.lexer.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import static com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes.*;
|
||||
|
||||
|
||||
/**
|
||||
* This class is a scanner generated by
|
||||
* <a href="http://www.jflex.de/">JFlex</a> 1.4.3
|
||||
* on 12.03.15 21:27 from the specification file
|
||||
* <tt>C:/work/ultimate/community/python/gen/com/jetbrains/commandInterface/gnuCommandLine/_CommandLineLexer.flex</tt>
|
||||
*/
|
||||
public class _CommandLineLexer implements FlexLexer {
|
||||
/** initial size of the lookahead buffer */
|
||||
private static final int ZZ_BUFFERSIZE = 16384;
|
||||
|
||||
/** lexical states */
|
||||
public static final int YYINITIAL = 0;
|
||||
|
||||
/**
|
||||
* ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
|
||||
* ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
|
||||
* at the beginning of a line
|
||||
* l is of the form l = 2*k, k a non negative integer
|
||||
*/
|
||||
private static final int ZZ_LEXSTATE[] = {
|
||||
0, 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Translates characters to character classes
|
||||
*/
|
||||
private static final String ZZ_CMAP_PACKED =
|
||||
"\11\0\1\1\1\1\1\0\1\1\1\1\22\0\1\1\14\0\1\13"+
|
||||
"\1\5\1\7\12\11\1\5\2\0\1\14\3\0\32\4\1\0\1\6"+
|
||||
"\2\0\1\3\1\0\32\4\1\0\1\12\55\0\1\2\12\0\1\2"+
|
||||
"\4\0\1\2\5\0\27\2\1\0\37\2\1\0\u013f\2\31\0\162\2"+
|
||||
"\4\0\14\2\16\0\5\2\11\0\1\2\213\0\1\2\13\0\1\2"+
|
||||
"\1\0\3\2\1\0\1\2\1\0\24\2\1\0\54\2\1\0\46\2"+
|
||||
"\1\0\5\2\4\0\202\2\10\0\105\2\1\0\46\2\2\0\2\2"+
|
||||
"\6\0\20\2\41\0\46\2\2\0\1\2\7\0\47\2\110\0\33\2"+
|
||||
"\5\0\3\2\56\0\32\2\5\0\13\2\25\0\12\10\4\0\2\2"+
|
||||
"\1\0\143\2\1\0\1\2\17\0\2\2\7\0\2\2\12\10\3\2"+
|
||||
"\2\0\1\2\20\0\1\2\1\0\36\2\35\0\3\2\60\0\46\2"+
|
||||
"\13\0\1\2\u0152\0\66\2\3\0\1\2\22\0\1\2\7\0\12\2"+
|
||||
"\4\0\12\10\25\0\10\2\2\0\2\2\2\0\26\2\1\0\7\2"+
|
||||
"\1\0\1\2\3\0\4\2\3\0\1\2\36\0\2\2\1\0\3\2"+
|
||||
"\4\0\12\10\2\2\23\0\6\2\4\0\2\2\2\0\26\2\1\0"+
|
||||
"\7\2\1\0\2\2\1\0\2\2\1\0\2\2\37\0\4\2\1\0"+
|
||||
"\1\2\7\0\12\10\2\0\3\2\20\0\11\2\1\0\3\2\1\0"+
|
||||
"\26\2\1\0\7\2\1\0\2\2\1\0\5\2\3\0\1\2\22\0"+
|
||||
"\1\2\17\0\2\2\4\0\12\10\25\0\10\2\2\0\2\2\2\0"+
|
||||
"\26\2\1\0\7\2\1\0\2\2\1\0\5\2\3\0\1\2\36\0"+
|
||||
"\2\2\1\0\3\2\4\0\12\10\1\0\1\2\21\0\1\2\1\0"+
|
||||
"\6\2\3\0\3\2\1\0\4\2\3\0\2\2\1\0\1\2\1\0"+
|
||||
"\2\2\3\0\2\2\3\0\3\2\3\0\10\2\1\0\3\2\55\0"+
|
||||
"\11\10\25\0\10\2\1\0\3\2\1\0\27\2\1\0\12\2\1\0"+
|
||||
"\5\2\46\0\2\2\4\0\12\10\25\0\10\2\1\0\3\2\1\0"+
|
||||
"\27\2\1\0\12\2\1\0\5\2\3\0\1\2\40\0\1\2\1\0"+
|
||||
"\2\2\4\0\12\10\25\0\10\2\1\0\3\2\1\0\27\2\1\0"+
|
||||
"\20\2\46\0\2\2\4\0\12\10\25\0\22\2\3\0\30\2\1\0"+
|
||||
"\11\2\1\0\1\2\2\0\7\2\72\0\60\2\1\0\2\2\14\0"+
|
||||
"\7\2\11\0\12\10\47\0\2\2\1\0\1\2\2\0\2\2\1\0"+
|
||||
"\1\2\2\0\1\2\6\0\4\2\1\0\7\2\1\0\3\2\1\0"+
|
||||
"\1\2\1\0\1\2\2\0\2\2\1\0\4\2\1\0\2\2\11\0"+
|
||||
"\1\2\2\0\5\2\1\0\1\2\11\0\12\10\2\0\2\2\42\0"+
|
||||
"\1\2\37\0\12\10\26\0\10\2\1\0\42\2\35\0\4\2\164\0"+
|
||||
"\42\2\1\0\5\2\1\0\2\2\25\0\12\10\6\0\6\2\112\0"+
|
||||
"\46\2\12\0\51\2\7\0\132\2\5\0\104\2\5\0\122\2\6\0"+
|
||||
"\7\2\1\0\77\2\1\0\1\2\1\0\4\2\2\0\7\2\1\0"+
|
||||
"\1\2\1\0\4\2\2\0\47\2\1\0\1\2\1\0\4\2\2\0"+
|
||||
"\37\2\1\0\1\2\1\0\4\2\2\0\7\2\1\0\1\2\1\0"+
|
||||
"\4\2\2\0\7\2\1\0\7\2\1\0\27\2\1\0\37\2\1\0"+
|
||||
"\1\2\1\0\4\2\2\0\7\2\1\0\47\2\1\0\23\2\16\0"+
|
||||
"\11\10\56\0\125\2\14\0\u026c\2\2\0\10\2\12\0\32\2\5\0"+
|
||||
"\113\2\25\0\15\2\1\0\4\2\16\0\22\2\16\0\22\2\16\0"+
|
||||
"\15\2\1\0\3\2\17\0\64\2\43\0\1\2\4\0\1\2\3\0"+
|
||||
"\12\10\46\0\12\10\6\0\130\2\10\0\51\2\127\0\35\2\51\0"+
|
||||
"\12\10\36\2\2\0\5\2\u038b\0\154\2\224\0\234\2\4\0\132\2"+
|
||||
"\6\0\26\2\2\0\6\2\2\0\46\2\2\0\6\2\2\0\10\2"+
|
||||
"\1\0\1\2\1\0\1\2\1\0\1\2\1\0\37\2\2\0\65\2"+
|
||||
"\1\0\7\2\1\0\1\2\3\0\3\2\1\0\7\2\3\0\4\2"+
|
||||
"\2\0\6\2\4\0\15\2\5\0\3\2\1\0\7\2\164\0\1\2"+
|
||||
"\15\0\1\2\202\0\1\2\4\0\1\2\2\0\12\2\1\0\1\2"+
|
||||
"\3\0\5\2\6\0\1\2\1\0\1\2\1\0\1\2\1\0\4\2"+
|
||||
"\1\0\3\2\1\0\7\2\3\0\3\2\5\0\5\2\u0ebb\0\2\2"+
|
||||
"\52\0\5\2\5\0\2\2\4\0\126\2\6\0\3\2\1\0\132\2"+
|
||||
"\1\0\4\2\5\0\50\2\4\0\136\2\21\0\30\2\70\0\20\2"+
|
||||
"\u0200\0\u19b6\2\112\0\u51a6\2\132\0\u048d\2\u0773\0\u2ba4\2\u215c\0\u012e\2"+
|
||||
"\2\0\73\2\225\0\7\2\14\0\5\2\5\0\1\2\1\0\12\2"+
|
||||
"\1\0\15\2\1\0\5\2\1\0\1\2\1\0\2\2\1\0\2\2"+
|
||||
"\1\0\154\2\41\0\u016b\2\22\0\100\2\2\0\66\2\50\0\14\2"+
|
||||
"\164\0\5\2\1\0\207\2\23\0\12\10\7\0\32\2\6\0\32\2"+
|
||||
"\13\0\131\2\3\0\6\2\2\0\6\2\2\0\6\2\2\0\3\2"+
|
||||
"\43\0";
|
||||
|
||||
/**
|
||||
* Translates characters to character classes
|
||||
*/
|
||||
private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED);
|
||||
|
||||
/**
|
||||
* Translates DFA states to action switch labels.
|
||||
*/
|
||||
private static final int [] ZZ_ACTION = zzUnpackAction();
|
||||
|
||||
private static final String ZZ_ACTION_PACKED_0 =
|
||||
"\1\0\1\1\1\2\1\3\1\4\1\1\1\5\1\0"+
|
||||
"\1\6\2\0\1\7";
|
||||
|
||||
private static int [] zzUnpackAction() {
|
||||
int [] result = new int[12];
|
||||
int offset = 0;
|
||||
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int zzUnpackAction(String packed, int offset, int [] result) {
|
||||
int i = 0; /* index in packed string */
|
||||
int j = offset; /* index in unpacked array */
|
||||
int l = packed.length();
|
||||
while (i < l) {
|
||||
int count = packed.charAt(i++);
|
||||
int value = packed.charAt(i++);
|
||||
do result[j++] = value; while (--count > 0);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translates a state to a row index in the transition table
|
||||
*/
|
||||
private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
|
||||
|
||||
private static final String ZZ_ROWMAP_PACKED_0 =
|
||||
"\0\0\0\15\0\32\0\47\0\64\0\101\0\15\0\116"+
|
||||
"\0\15\0\133\0\150\0\165";
|
||||
|
||||
private static int [] zzUnpackRowMap() {
|
||||
int [] result = new int[12];
|
||||
int offset = 0;
|
||||
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int zzUnpackRowMap(String packed, int offset, int [] result) {
|
||||
int i = 0; /* index in packed string */
|
||||
int j = offset; /* index in unpacked array */
|
||||
int l = packed.length();
|
||||
while (i < l) {
|
||||
int high = packed.charAt(i++) << 16;
|
||||
result[j++] = high | packed.charAt(i++);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
/**
|
||||
* The transition table of the DFA
|
||||
*/
|
||||
private static final int [] ZZ_TRANS = zzUnpackTrans();
|
||||
|
||||
private static final String ZZ_TRANS_PACKED_0 =
|
||||
"\1\2\1\3\1\4\1\2\1\4\3\2\2\5\1\2"+
|
||||
"\1\6\1\7\16\0\1\3\16\0\5\4\1\0\1\4"+
|
||||
"\6\0\3\5\1\10\2\0\1\5\5\0\1\11\1\0"+
|
||||
"\1\11\6\0\1\12\13\0\1\13\4\0\1\14\1\0"+
|
||||
"\1\14\17\0\1\5\10\0\2\14\4\0\1\14\1\0"+
|
||||
"\1\14\1\0";
|
||||
|
||||
private static int [] zzUnpackTrans() {
|
||||
int [] result = new int[130];
|
||||
int offset = 0;
|
||||
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int zzUnpackTrans(String packed, int offset, int [] result) {
|
||||
int i = 0; /* index in packed string */
|
||||
int j = offset; /* index in unpacked array */
|
||||
int l = packed.length();
|
||||
while (i < l) {
|
||||
int count = packed.charAt(i++);
|
||||
int value = packed.charAt(i++);
|
||||
value--;
|
||||
do result[j++] = value; while (--count > 0);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
/* error codes */
|
||||
private static final int ZZ_UNKNOWN_ERROR = 0;
|
||||
private static final int ZZ_NO_MATCH = 1;
|
||||
private static final int ZZ_PUSHBACK_2BIG = 2;
|
||||
private static final char[] EMPTY_BUFFER = new char[0];
|
||||
private static final int YYEOF = -1;
|
||||
private static java.io.Reader zzReader = null; // Fake
|
||||
|
||||
/* error messages for the codes above */
|
||||
private static final String ZZ_ERROR_MSG[] = {
|
||||
"Unkown internal scanner error",
|
||||
"Error: could not match input",
|
||||
"Error: pushback value was too large"
|
||||
};
|
||||
|
||||
/**
|
||||
* ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
|
||||
*/
|
||||
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
|
||||
|
||||
private static final String ZZ_ATTRIBUTE_PACKED_0 =
|
||||
"\1\0\1\11\4\1\1\11\1\0\1\11\2\0\1\1";
|
||||
|
||||
private static int [] zzUnpackAttribute() {
|
||||
int [] result = new int[12];
|
||||
int offset = 0;
|
||||
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int zzUnpackAttribute(String packed, int offset, int [] result) {
|
||||
int i = 0; /* index in packed string */
|
||||
int j = offset; /* index in unpacked array */
|
||||
int l = packed.length();
|
||||
while (i < l) {
|
||||
int count = packed.charAt(i++);
|
||||
int value = packed.charAt(i++);
|
||||
do result[j++] = value; while (--count > 0);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
/** the current state of the DFA */
|
||||
private int zzState;
|
||||
|
||||
/** the current lexical state */
|
||||
private int zzLexicalState = YYINITIAL;
|
||||
|
||||
/** this buffer contains the current text to be matched and is
|
||||
the source of the yytext() string */
|
||||
private CharSequence zzBuffer = "";
|
||||
|
||||
/** this buffer may contains the current text array to be matched when it is cheap to acquire it */
|
||||
private char[] zzBufferArray;
|
||||
|
||||
/** the textposition at the last accepting state */
|
||||
private int zzMarkedPos;
|
||||
|
||||
/** the textposition at the last state to be included in yytext */
|
||||
private int zzPushbackPos;
|
||||
|
||||
/** the current text position in the buffer */
|
||||
private int zzCurrentPos;
|
||||
|
||||
/** startRead marks the beginning of the yytext() string in the buffer */
|
||||
private int zzStartRead;
|
||||
|
||||
/** endRead marks the last character in the buffer, that has been read
|
||||
from input */
|
||||
private int zzEndRead;
|
||||
|
||||
/**
|
||||
* zzAtBOL == true <=> the scanner is currently at the beginning of a line
|
||||
*/
|
||||
private boolean zzAtBOL = true;
|
||||
|
||||
/** zzAtEOF == true <=> the scanner is at the EOF */
|
||||
private boolean zzAtEOF;
|
||||
|
||||
/* user code: */
|
||||
public _CommandLineLexer() {
|
||||
this((java.io.Reader)null);
|
||||
}
|
||||
|
||||
|
||||
public _CommandLineLexer(java.io.Reader in) {
|
||||
this.zzReader = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new scanner.
|
||||
* There is also java.io.Reader version of this constructor.
|
||||
*
|
||||
* @param in the java.io.Inputstream to read input from.
|
||||
*/
|
||||
public _CommandLineLexer(java.io.InputStream in) {
|
||||
this(new java.io.InputStreamReader(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks the compressed character translation table.
|
||||
*
|
||||
* @param packed the packed character translation table
|
||||
* @return the unpacked character translation table
|
||||
*/
|
||||
private static char [] zzUnpackCMap(String packed) {
|
||||
char [] map = new char[0x10000];
|
||||
int i = 0; /* index in packed string */
|
||||
int j = 0; /* index in unpacked array */
|
||||
while (i < 1222) {
|
||||
int count = packed.charAt(i++);
|
||||
char value = packed.charAt(i++);
|
||||
do map[j++] = value; while (--count > 0);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public final int getTokenStart(){
|
||||
return zzStartRead;
|
||||
}
|
||||
|
||||
public final int getTokenEnd(){
|
||||
return getTokenStart() + yylength();
|
||||
}
|
||||
|
||||
public void reset(CharSequence buffer, int start, int end,int initialState){
|
||||
zzBuffer = buffer;
|
||||
zzBufferArray = com.intellij.util.text.CharArrayUtil.fromSequenceWithoutCopying(buffer);
|
||||
zzCurrentPos = zzMarkedPos = zzStartRead = start;
|
||||
zzPushbackPos = 0;
|
||||
zzAtEOF = false;
|
||||
zzAtBOL = true;
|
||||
zzEndRead = end;
|
||||
yybegin(initialState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refills the input buffer.
|
||||
*
|
||||
* @return <code>false</code>, iff there was new input.
|
||||
*
|
||||
* @exception java.io.IOException if any I/O-Error occurs
|
||||
*/
|
||||
private boolean zzRefill() throws java.io.IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current lexical state.
|
||||
*/
|
||||
public final int yystate() {
|
||||
return zzLexicalState;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enters a new lexical state
|
||||
*
|
||||
* @param newState the new lexical state
|
||||
*/
|
||||
public final void yybegin(int newState) {
|
||||
zzLexicalState = newState;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the text matched by the current regular expression.
|
||||
*/
|
||||
public final CharSequence yytext() {
|
||||
return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the character at position <tt>pos</tt> from the
|
||||
* matched text.
|
||||
*
|
||||
* It is equivalent to yytext().charAt(pos), but faster
|
||||
*
|
||||
* @param pos the position of the character to fetch.
|
||||
* A value from 0 to yylength()-1.
|
||||
*
|
||||
* @return the character at position pos
|
||||
*/
|
||||
public final char yycharat(int pos) {
|
||||
return zzBufferArray != null ? zzBufferArray[zzStartRead+pos]:zzBuffer.charAt(zzStartRead+pos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the length of the matched text region.
|
||||
*/
|
||||
public final int yylength() {
|
||||
return zzMarkedPos-zzStartRead;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reports an error that occured while scanning.
|
||||
*
|
||||
* In a wellformed scanner (no or only correct usage of
|
||||
* yypushback(int) and a match-all fallback rule) this method
|
||||
* will only be called with things that "Can't Possibly Happen".
|
||||
* If this method is called, something is seriously wrong
|
||||
* (e.g. a JFlex bug producing a faulty scanner etc.).
|
||||
*
|
||||
* Usual syntax/scanner level error handling should be done
|
||||
* in error fallback rules.
|
||||
*
|
||||
* @param errorCode the code of the errormessage to display
|
||||
*/
|
||||
private void zzScanError(int errorCode) {
|
||||
String message;
|
||||
try {
|
||||
message = ZZ_ERROR_MSG[errorCode];
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pushes the specified amount of characters back into the input stream.
|
||||
*
|
||||
* They will be read again by then next call of the scanning method
|
||||
*
|
||||
* @param number the number of characters to be read again.
|
||||
* This number must not be greater than yylength()!
|
||||
*/
|
||||
public void yypushback(int number) {
|
||||
if ( number > yylength() )
|
||||
zzScanError(ZZ_PUSHBACK_2BIG);
|
||||
|
||||
zzMarkedPos -= number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resumes scanning until the next regular expression is matched,
|
||||
* the end of input is encountered or an I/O-Error occurs.
|
||||
*
|
||||
* @return the next token
|
||||
* @exception java.io.IOException if any I/O-Error occurs
|
||||
*/
|
||||
public IElementType advance() throws java.io.IOException {
|
||||
int zzInput;
|
||||
int zzAction;
|
||||
|
||||
// cached fields:
|
||||
int zzCurrentPosL;
|
||||
int zzMarkedPosL;
|
||||
int zzEndReadL = zzEndRead;
|
||||
CharSequence zzBufferL = zzBuffer;
|
||||
char[] zzBufferArrayL = zzBufferArray;
|
||||
char [] zzCMapL = ZZ_CMAP;
|
||||
|
||||
int [] zzTransL = ZZ_TRANS;
|
||||
int [] zzRowMapL = ZZ_ROWMAP;
|
||||
int [] zzAttrL = ZZ_ATTRIBUTE;
|
||||
|
||||
while (true) {
|
||||
zzMarkedPosL = zzMarkedPos;
|
||||
|
||||
zzAction = -1;
|
||||
|
||||
zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
|
||||
|
||||
zzState = ZZ_LEXSTATE[zzLexicalState];
|
||||
|
||||
|
||||
zzForAction: {
|
||||
while (true) {
|
||||
|
||||
if (zzCurrentPosL < zzEndReadL)
|
||||
zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++));
|
||||
else if (zzAtEOF) {
|
||||
zzInput = YYEOF;
|
||||
break zzForAction;
|
||||
}
|
||||
else {
|
||||
// store back cached positions
|
||||
zzCurrentPos = zzCurrentPosL;
|
||||
zzMarkedPos = zzMarkedPosL;
|
||||
boolean eof = zzRefill();
|
||||
// get translated positions and possibly new buffer
|
||||
zzCurrentPosL = zzCurrentPos;
|
||||
zzMarkedPosL = zzMarkedPos;
|
||||
zzBufferL = zzBuffer;
|
||||
zzEndReadL = zzEndRead;
|
||||
if (eof) {
|
||||
zzInput = YYEOF;
|
||||
break zzForAction;
|
||||
}
|
||||
else {
|
||||
zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++));
|
||||
}
|
||||
}
|
||||
int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ];
|
||||
if (zzNext == -1) break zzForAction;
|
||||
zzState = zzNext;
|
||||
|
||||
int zzAttributes = zzAttrL[zzState];
|
||||
if ( (zzAttributes & 1) == 1 ) {
|
||||
zzAction = zzState;
|
||||
zzMarkedPosL = zzCurrentPosL;
|
||||
if ( (zzAttributes & 8) == 8 ) break zzForAction;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// store back cached position
|
||||
zzMarkedPos = zzMarkedPosL;
|
||||
|
||||
switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
|
||||
case 4:
|
||||
{ return LITERAL_STARTS_FROM_DIGIT;
|
||||
}
|
||||
case 8: break;
|
||||
case 1:
|
||||
{ return com.intellij.psi.TokenType.BAD_CHARACTER;
|
||||
}
|
||||
case 9: break;
|
||||
case 3:
|
||||
{ return LITERAL_STARTS_FROM_LETTER;
|
||||
}
|
||||
case 10: break;
|
||||
case 7:
|
||||
{ return LONG_OPTION_NAME_TOKEN;
|
||||
}
|
||||
case 11: break;
|
||||
case 5:
|
||||
{ return EQ;
|
||||
}
|
||||
case 12: break;
|
||||
case 6:
|
||||
{ return SHORT_OPTION_NAME_TOKEN;
|
||||
}
|
||||
case 13: break;
|
||||
case 2:
|
||||
{ return com.intellij.psi.TokenType.WHITE_SPACE;
|
||||
}
|
||||
case 14: break;
|
||||
default:
|
||||
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
|
||||
zzAtEOF = true;
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
zzScanError(ZZ_NO_MATCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
public interface CommandLineArgument extends PsiElement {
|
||||
|
||||
@Nullable
|
||||
PsiElement getLiteralStartsFromDigit();
|
||||
|
||||
@Nullable
|
||||
PsiElement getLiteralStartsFromLetter();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
public interface CommandLineCommand extends PsiElement {
|
||||
|
||||
@NotNull
|
||||
PsiElement getLiteralStartsFromLetter();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
public interface CommandLineOption extends PsiElement {
|
||||
|
||||
@Nullable
|
||||
PsiElement getLongOptionNameToken();
|
||||
|
||||
@Nullable
|
||||
PsiElement getShortOptionNameToken();
|
||||
|
||||
@Nullable
|
||||
@NonNls
|
||||
String getOptionName();
|
||||
|
||||
boolean isLong();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi;
|
||||
|
||||
import org.jetbrains.annotations.*;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
public class CommandLineVisitor extends PsiElementVisitor {
|
||||
|
||||
public void visitArgument(@NotNull CommandLineArgument o) {
|
||||
visitPsiElement(o);
|
||||
}
|
||||
|
||||
public void visitCommand(@NotNull CommandLineCommand o) {
|
||||
visitPsiElement(o);
|
||||
}
|
||||
|
||||
public void visitOption(@NotNull CommandLineOption o) {
|
||||
visitPsiElement(o);
|
||||
}
|
||||
|
||||
public void visitPsiElement(@NotNull PsiElement o) {
|
||||
visitElement(o);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi.impl;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.*;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import static com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes.*;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.CommandLineElement;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.*;
|
||||
|
||||
public class CommandLineArgumentImpl extends CommandLineElement implements CommandLineArgument {
|
||||
|
||||
public CommandLineArgumentImpl(ASTNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
public void accept(@NotNull PsiElementVisitor visitor) {
|
||||
if (visitor instanceof CommandLineVisitor) ((CommandLineVisitor)visitor).visitArgument(this);
|
||||
else super.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiElement getLiteralStartsFromDigit() {
|
||||
return findChildByType(LITERAL_STARTS_FROM_DIGIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiElement getLiteralStartsFromLetter() {
|
||||
return findChildByType(LITERAL_STARTS_FROM_LETTER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi.impl;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.*;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import static com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes.*;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.CommandLineElement;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.*;
|
||||
|
||||
public class CommandLineCommandImpl extends CommandLineElement implements CommandLineCommand {
|
||||
|
||||
public CommandLineCommandImpl(ASTNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
public void accept(@NotNull PsiElementVisitor visitor) {
|
||||
if (visitor instanceof CommandLineVisitor) ((CommandLineVisitor)visitor).visitCommand(this);
|
||||
else super.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElement getLiteralStartsFromLetter() {
|
||||
return findNotNullChildByType(LITERAL_STARTS_FROM_LETTER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi.impl;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.*;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import static com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes.*;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.CommandLineElement;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.*;
|
||||
|
||||
public class CommandLineOptionImpl extends CommandLineElement implements CommandLineOption {
|
||||
|
||||
public CommandLineOptionImpl(ASTNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
public void accept(@NotNull PsiElementVisitor visitor) {
|
||||
if (visitor instanceof CommandLineVisitor) ((CommandLineVisitor)visitor).visitOption(this);
|
||||
else super.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiElement getLongOptionNameToken() {
|
||||
return findChildByType(LONG_OPTION_NAME_TOKEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiElement getShortOptionNameToken() {
|
||||
return findChildByType(SHORT_OPTION_NAME_TOKEN);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NonNls
|
||||
public String getOptionName() {
|
||||
return CommandLinePsiImplUtils.getOptionName(this);
|
||||
}
|
||||
|
||||
public boolean isLong() {
|
||||
return CommandLinePsiImplUtils.isLong(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,7 @@ __author__ = 'Ilya.Kazakevich'
|
||||
version = LooseVersion(django.get_version())
|
||||
assert version < LooseVersion('1.8a'), "Only Django <1.8 is supported now"
|
||||
# Some django versions require setup
|
||||
if django.setup:
|
||||
if hasattr(django, 'setup'):
|
||||
django.setup()
|
||||
dumper = _xml.XmlDumper()
|
||||
_optparse.report_data(dumper)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<body>
|
||||
<strong>Command line (commands, arguments and options) inspection.</strong>
|
||||
<!-- tooltip end -->
|
||||
<p>This inspection checks command you type in command console or command file. It helps you to make sure arguments are on their
|
||||
places, option names are correct as well as arguments, provided for options.</p>
|
||||
<p>Do not disable it if you are going to use command-line interfaces like manage.py in Django</p>
|
||||
</body>
|
||||
</html>
|
||||
26
python/src/META-INF/gnu-command-line.xml
Normal file
26
python/src/META-INF/gnu-command-line.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<idea-plugin version="2">
|
||||
|
||||
<!--
|
||||
Extension points to support gnu command line language.
|
||||
See {@link com.jetbrains.commandInterface.gnuCommandLine} package
|
||||
-->
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
|
||||
|
||||
<fileTypeFactory implementation="com.jetbrains.commandInterface.gnuCommandLine.CommandLineFileTypeFactory"/>
|
||||
<lang.parserDefinition language="CommandLine"
|
||||
implementationClass="com.jetbrains.commandInterface.gnuCommandLine.CommandLineParserDefinition"/>
|
||||
<lang.syntaxHighlighterFactory key="CommandLine"
|
||||
implementationClass="com.jetbrains.commandInterface.gnuCommandLine.CommandLineSyntaxHighlighterFactory"/>
|
||||
<psi.referenceContributor implementation="com.jetbrains.commandInterface.gnuCommandLine.CommandLineReferenceContributor"/>
|
||||
<lang.elementManipulator forClass="com.jetbrains.commandInterface.gnuCommandLine.CommandLineElement"
|
||||
implementationClass="com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementManipulator"/>
|
||||
<localInspection language="CommandLine" shortName="CommandLineInspection" displayName="Command-line inspection"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
groupKey="INSP.GROUP.python"
|
||||
groupBundle="com.jetbrains.python.PyBundle"
|
||||
implementationClass="com.jetbrains.commandInterface.gnuCommandLine.CommandLineInspection"/>
|
||||
<lang.documentationProvider language="CommandLine"
|
||||
implementationClass="com.jetbrains.commandInterface.gnuCommandLine.CommandLineDocumentationProvider"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -1,4 +1,4 @@
|
||||
<idea-plugin version="2">
|
||||
<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<!-- Components and extensions declared in this file work both in PyCharm and Python plugin.
|
||||
Both Community and Professional editions. -->
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
<module value="com.intellij.modules.python"/>
|
||||
|
||||
<!-- TODO: Doc -->
|
||||
<xi:include href="/META-INF/gnu-command-line.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<library.type implementation="com.jetbrains.python.library.PythonLibraryType"/>
|
||||
<renameHandler implementation="com.jetbrains.python.magicLiteral.PyMagicLiteralRenameHandler"/>
|
||||
|
||||
154
python/src/com/jetbrains/commandInterface/command/Argument.java
Normal file
154
python/src/com/jetbrains/commandInterface/command/Argument.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.commandInterface.command;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: Support regex validation as well
|
||||
|
||||
/**
|
||||
* Command argument (positional or option argument)
|
||||
* This class represents command argument, not its value.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class Argument {
|
||||
/**
|
||||
* Argument help user-readable text
|
||||
*/
|
||||
@NotNull
|
||||
private final String myHelpText;
|
||||
/**
|
||||
* List of values argument may have. Null if any value is possible.
|
||||
*/
|
||||
@Nullable
|
||||
private final List<String> myAvailableValues;
|
||||
@Nullable
|
||||
private final ArgumentType myType;
|
||||
|
||||
|
||||
/**
|
||||
* @param helpText Argument help user-readable text
|
||||
*/
|
||||
public Argument(@NotNull final String helpText) {
|
||||
this(helpText, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type Argument value type. Null if any type is possible.
|
||||
*/
|
||||
public Argument(@NotNull final ArgumentType type) {
|
||||
this("", type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param helpText Argument help user-readable text
|
||||
* @param type Argument value type. Null if any type is possible.
|
||||
*/
|
||||
public Argument(@NotNull final String helpText,
|
||||
@Nullable final ArgumentType type) {
|
||||
this(helpText, null, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param availableValues List of values argument may have. Null if any value is possible.
|
||||
*/
|
||||
public Argument(@Nullable final List<String> availableValues) {
|
||||
this("", availableValues, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param helpText Argument help user-readable text
|
||||
* @param availableValues List of values argument may have. Null if any value is possible.
|
||||
*/
|
||||
public Argument(@NotNull final String helpText, @Nullable final List<String> availableValues) {
|
||||
this(helpText, availableValues, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param helpText Argument help user-readable text
|
||||
* @param availableValues List of values argument may have. Null if any value is possible.
|
||||
* @param type Argument value type. Null if any type is possible.
|
||||
*/
|
||||
public Argument(@NotNull final String helpText,
|
||||
@Nullable final List<String> availableValues,
|
||||
@Nullable final ArgumentType type) {
|
||||
myHelpText = helpText;
|
||||
myAvailableValues = (availableValues == null ? null : new ArrayList<String>(availableValues));
|
||||
myType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Argument help user-readable text
|
||||
*/
|
||||
@NotNull
|
||||
public String getHelpText() {
|
||||
return myHelpText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of values argument may have. Null if any value is possible.
|
||||
*/
|
||||
@Nullable
|
||||
public List<String> getAvailableValues() {
|
||||
return (myAvailableValues == null ? null : Collections.unmodifiableList(myAvailableValues));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates argument value. Argument tries its best to validate value based on information, provided by constructor.
|
||||
*
|
||||
* @param value value to check
|
||||
* @return true if argument may have this value.
|
||||
*/
|
||||
public boolean isValid(@NotNull final String value) {
|
||||
if (!isTypeValid(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (myAvailableValues == null) {
|
||||
return true;
|
||||
}
|
||||
return myAvailableValues.contains(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures value conforms type (if known)
|
||||
*
|
||||
* @param value value to check
|
||||
* @return false if type is known and it differs from value
|
||||
*/
|
||||
private boolean isTypeValid(@NotNull final String value) {
|
||||
// We only check integer for now
|
||||
if (myType == ArgumentType.INTEGER) {
|
||||
try {
|
||||
// We just getCommandLineInfo it to get exception
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
Integer.parseInt(value);
|
||||
}
|
||||
catch (final NumberFormatException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.command;
|
||||
|
||||
/**
|
||||
* Argument type to be used with {@link OptionTypedArgumentInfo}
|
||||
* Argument type to be used with {@link Argument}
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public enum OptionArgumentType {
|
||||
public enum ArgumentType {
|
||||
/**
|
||||
* String (actually, anything)
|
||||
*/
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.command;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.command;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -36,10 +36,13 @@ public interface Command {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* @param tryCutOutArguments Try to remove information about arguments from help text (i.e. "[file] removes file" -> "removes file").
|
||||
* Command may or may not support it.
|
||||
* It should ignore argument if it does not know how to cut out argument info.
|
||||
* @return Command readable help text
|
||||
*/
|
||||
@Nullable
|
||||
String getHelp();
|
||||
String getHelp(boolean tryCutOutArguments);
|
||||
|
||||
|
||||
/**
|
||||
@@ -57,7 +60,7 @@ public interface Command {
|
||||
/**
|
||||
* Execute command
|
||||
*
|
||||
* @param module module to execute command against
|
||||
* @param module module to execute command against
|
||||
* @param parameters command's arguments and options (just like entered by user but splitted by space)
|
||||
*/
|
||||
void execute(@NotNull final Module module, @NotNull final List<String> parameters);
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.command;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.command;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.command;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -37,7 +37,7 @@ public final class Option {
|
||||
@NotNull
|
||||
private final List<String> myShortNames = new ArrayList<String>();
|
||||
@Nullable
|
||||
private final Pair<Integer, OptionArgumentInfo> myArgumentAndQuantity;
|
||||
private final Pair<Integer, Argument> myArgumentAndQuantity;
|
||||
@NotNull
|
||||
private final String myHelp;
|
||||
|
||||
@@ -47,7 +47,7 @@ public final class Option {
|
||||
* @param shortNames option short names
|
||||
* @param longNames option long names
|
||||
*/
|
||||
public Option(@Nullable final Pair<Integer, OptionArgumentInfo> argumentAndQuantity,
|
||||
public Option(@Nullable final Pair<Integer, Argument> argumentAndQuantity,
|
||||
@NotNull final String help,
|
||||
@NotNull final Collection<String> shortNames,
|
||||
@NotNull final Collection<String> longNames) {
|
||||
@@ -85,11 +85,12 @@ public final class Option {
|
||||
return Collections.unmodifiableList(myShortNames);
|
||||
}
|
||||
|
||||
// TODO: USe "known arguments info" to prevent copy/paste
|
||||
/**
|
||||
* @return if option accepts argument -- pair of [argument_quantity, its_type_info]. Null otherwise.
|
||||
* @return if option accepts argument -- pair of [argument_quantity, argument]. Null otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public Pair<Integer, OptionArgumentInfo> getArgumentAndQuantity() {
|
||||
public Pair<Integer, Argument> getArgumentAndQuantity() {
|
||||
return myArgumentAndQuantity;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.command;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -13,24 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Engine to execute command line
|
||||
* <h1>Command with arguments and options.</h1>
|
||||
* <p>
|
||||
* Each {@link com.jetbrains.commandInterface.command.Command} may have one or more positional {@link com.jetbrains.commandInterface.command.Argument arguments}
|
||||
* and several {@link com.jetbrains.commandInterface.command.Option options} (with arguments as well).
|
||||
* You need to implemenet {@link com.jetbrains.commandInterface.command.Command} first.
|
||||
* </p>
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface Executor {
|
||||
/**
|
||||
* @return information about what to execute (like command name)
|
||||
*/
|
||||
@Nullable
|
||||
String getExecutionDescription();
|
||||
|
||||
/**
|
||||
* Execute command!
|
||||
*/
|
||||
void execute();
|
||||
}
|
||||
package com.jetbrains.commandInterface.command;
|
||||
@@ -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.commandInterface.console;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.CommandLineLanguage;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Delegates console execution to command.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class CommandExecutor implements Consumer<String> {
|
||||
@NotNull
|
||||
private static final Pattern EMPTY_SPACE = Pattern.compile("\\s+");
|
||||
@NotNull
|
||||
private final Collection<Command> myCommands = new ArrayList<Command>();
|
||||
@NotNull
|
||||
private final Module myModule;
|
||||
|
||||
CommandExecutor(@NotNull final Collection<Command> commands, @NotNull final Module module) {
|
||||
myCommands.addAll(commands);
|
||||
myModule = module;
|
||||
}
|
||||
@Override
|
||||
public final void consume(final String t) {
|
||||
/**
|
||||
* We need to: 1) parse input 2) fetch command 3) split its arguments.
|
||||
*/
|
||||
final PsiFileFactory fileFactory = PsiFileFactory.getInstance(myModule.getProject());
|
||||
final CommandLineFile file = PyUtil.as(fileFactory.createFileFromText(CommandLineLanguage.INSTANCE, t), CommandLineFile.class);
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
final String commandName = file.getCommand();
|
||||
|
||||
for (final Command command : myCommands) {
|
||||
if (command.getName().equals(commandName)) {
|
||||
final List<String> argument = Arrays.asList(EMPTY_SPACE.split(file.getText()));
|
||||
// 1 because we need to command which is on the first place
|
||||
command.execute(myModule, argument.subList(1, argument.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.commandInterface.console;
|
||||
|
||||
import com.intellij.execution.console.LanguageConsoleBuilder;
|
||||
import com.intellij.execution.console.LanguageConsoleImpl;
|
||||
import com.intellij.execution.console.LanguageConsoleView;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowAnchor;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.ui.content.ContentManager;
|
||||
import com.intellij.ui.content.impl.ContentImpl;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.CommandLineLanguage;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Displays command-line console for user.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineConsole {
|
||||
|
||||
private CommandLineConsole() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and displays command-line console for user.
|
||||
*
|
||||
* @param module module to display console for.
|
||||
* @param consoleName Console name (would be used in prompt, history etc)
|
||||
* @param commandList list of commands available for this console. You may pass null here, but in this case no validation nor suggestion
|
||||
* would work. Additionaly, no executor would be registered, so you will need to use
|
||||
* {@link LanguageConsoleBuilder#registerExecuteAction(LanguageConsoleView, Consumer, String, String, Condition)}
|
||||
* by yourself passing this method result as arg to enable execution, history etc.
|
||||
* @return newly created console. You do not need to do anything with this value to display console: it will be displayed automatically
|
||||
*/
|
||||
@NotNull
|
||||
public static LanguageConsoleView createConsole(
|
||||
@NotNull final Module module,
|
||||
@NotNull final String consoleName,
|
||||
@Nullable final List<Command> commandList) {
|
||||
final Project project = module.getProject();
|
||||
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
|
||||
ToolWindow window = toolWindowManager.getToolWindow(consoleName);
|
||||
if (window == null) {
|
||||
window = toolWindowManager.registerToolWindow(consoleName, true, ToolWindowAnchor.BOTTOM);
|
||||
}
|
||||
window.activate(null);
|
||||
|
||||
final ContentManager contentManager = window.getContentManager();
|
||||
contentManager.removeAllContents(true);
|
||||
final LanguageConsoleView console = new LanguageConsoleImpl(project, "", CommandLineLanguage.INSTANCE);
|
||||
console.setPrompt(consoleName + " > ");
|
||||
console.setEditable(true);
|
||||
|
||||
final CommandLineFile file = PyUtil.as(console.getFile(), CommandLineFile.class);
|
||||
if (file != null && commandList != null) {
|
||||
file.setCommands(commandList);
|
||||
LanguageConsoleBuilder.registerExecuteAction(console, new CommandExecutor(commandList, module), consoleName, consoleName, null);
|
||||
}
|
||||
|
||||
|
||||
final Content content = new ContentImpl(console.getComponent(), "", true);
|
||||
contentManager.addContent(content);
|
||||
|
||||
showHiddenCommandWorkAround(console);
|
||||
return console;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Investigate why do we need this hack
|
||||
* For some reason console is not displayed correctly unless we type something to it (i.e. space)
|
||||
*
|
||||
* @param console console to type space
|
||||
*/
|
||||
private static void showHiddenCommandWorkAround(@NotNull final LanguageConsoleView console) {
|
||||
console.getComponent().setVisible(true);
|
||||
CommandProcessor.getInstance().executeCommand(null, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
console.getEditorDocument().insertString(0, " ");
|
||||
console.getComponent().grabFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,17 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This exception is thrown when command line can't be parsed
|
||||
* Package to display command line console on the bottom of the screen. It supports history, execution, syntax highlighting and so on.
|
||||
* Entry point is {@link com.jetbrains.commandInterface.console.CommandLineConsole}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see com.jetbrains.commandInterface.console.CommandLineConsole
|
||||
*/
|
||||
public class MalformedCommandLineException extends Exception {
|
||||
public MalformedCommandLineException(@NotNull final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
package com.jetbrains.commandInterface.console;
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.*;
|
||||
import com.jetbrains.commandInterface.command.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Ref to be injected in command line argument
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineArgumentReference extends CommandLineElementReference<CommandLineArgument> {
|
||||
CommandLineArgumentReference(@NotNull final CommandLineArgument element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
final ValidationResult validationResult = getValidationResult();
|
||||
if (validationResult == null) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
final Collection<LookupElement> result = new ArrayList<LookupElement>();
|
||||
final Collection<String> argumentValues = validationResult.getPossibleArgumentValues(getElement());
|
||||
|
||||
// priority is used to display args before options
|
||||
if (argumentValues != null) {
|
||||
for (final String value : argumentValues) {
|
||||
result.add(PrioritizedLookupElement.withPriority(LookupElementBuilder.create(value).withBoldness(true), 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!validationResult.isOptionArgument(getElement())) {
|
||||
for (final Option option : validationResult.getUnusedOptions()) {
|
||||
for (final String value : option.getAllNames()) {
|
||||
result.add(PrioritizedLookupElement.withPriority(LookupElementBuilder.create(value).withTailText(" :" + option.getHelp()), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ArrayUtil.toObjectArray(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineCommand;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Ref to be injected in command itself
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineCommandReference extends CommandLineElementReference<CommandLineCommand> {
|
||||
|
||||
CommandLineCommandReference(@NotNull final CommandLineCommand element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
final CommandLineFile file = getCommandLineFile();
|
||||
if (file == null) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
final List<Command> commands = file.getCommands();
|
||||
if (commands == null) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
final Collection<LookupElement> commandNames = new ArrayList<LookupElement>();
|
||||
|
||||
for (final Command command : commands) {
|
||||
LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(command.getName());
|
||||
final String help = command.getHelp(true);
|
||||
if (!StringUtil.isEmpty(help)) {
|
||||
lookupElementBuilder = lookupElementBuilder.withTailText(" :" + help);
|
||||
}
|
||||
commandNames.add(lookupElementBuilder);
|
||||
}
|
||||
|
||||
|
||||
return ArrayUtil.toObjectArray(commandNames);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.lang.documentation.DocumentationProvider;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides quick help for arguments
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineDocumentationProvider implements DocumentationProvider {
|
||||
@Nullable
|
||||
@Override
|
||||
public String getQuickNavigateInfo(final PsiElement element, final PsiElement originalElement) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getUrlFor(final PsiElement element, final PsiElement originalElement) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String generateDoc(final PsiElement element, @Nullable final PsiElement originalElement) {
|
||||
if (element instanceof CommandLineFile) {
|
||||
final Command command = ((CommandLineFile)element).findRealCommand();
|
||||
if (command != null) {
|
||||
return command.getHelp(false); // We do not need arguments info in help text
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getDocumentationElementForLookupItem(final PsiManager psiManager, final Object object, final PsiElement element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getDocumentationElementForLink(final PsiManager psiManager, final String link, final PsiElement context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -13,32 +13,28 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser.optParse;
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.extapi.psi.ASTWrapperPsiElement;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Deals with short-style options like -b or -f
|
||||
* Parent of all command line elements (enables reference injection, see {@link #getReferences()})
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class ShortOptionParser extends OptionParserRegexBased {
|
||||
/**
|
||||
* Short-style option regexp
|
||||
*/
|
||||
private static final Pattern SHORT_OPT_PATTERN = Pattern.compile("^(-[a-zA-Z0-9])([^ -])?");
|
||||
|
||||
ShortOptionParser() {
|
||||
super(SHORT_OPT_PATTERN);
|
||||
public class CommandLineElement extends ASTWrapperPsiElement {
|
||||
protected CommandLineElement(@NotNull final ASTNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Pair<String, String> getOptionTextAndNameFromMatcher(@NotNull final Matcher matcher) {
|
||||
return Pair.create(matcher.group(1), matcher.group(1));
|
||||
public final PsiReference[] getReferences() {
|
||||
// We need it to enable reference injection
|
||||
return ReferenceProvidersRegistry.getReferencesFromProviders(this);
|
||||
}
|
||||
}
|
||||
@@ -13,30 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser.optParse;
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.AbstractElementManipulator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Supports --long-option-style-with=value
|
||||
* Manipulator to support reference injection. Will fail with out of it.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class LongOptionParser extends OptionParserRegexBased {
|
||||
@NotNull
|
||||
private static final Pattern LONG_OPT_PATTERN = Pattern.compile("^((--[a-zA-Z0-9-]+)=?)");
|
||||
public final class CommandLineElementManipulator extends AbstractElementManipulator<CommandLineElement> {
|
||||
|
||||
LongOptionParser() {
|
||||
super(LONG_OPT_PATTERN);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Pair<String, String> getOptionTextAndNameFromMatcher(@NotNull final Matcher matcher) {
|
||||
return Pair.create(matcher.group(1), matcher.group(2));
|
||||
public CommandLineElement handleContentChange(@NotNull final CommandLineElement element,
|
||||
@NotNull final TextRange range,
|
||||
final String newContent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReferenceBase;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parent of all references injected to command line elements
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
abstract class CommandLineElementReference<T extends PsiElement> extends PsiReferenceBase<T> {
|
||||
protected CommandLineElementReference(@NotNull final T element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command line file this element sits in (if any)
|
||||
*/
|
||||
@Nullable
|
||||
protected final CommandLineFile getCommandLineFile() {
|
||||
return PsiTreeUtil.getParentOfType(getElement(), CommandLineFile.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command line validation result (if any)
|
||||
*/
|
||||
@Nullable
|
||||
protected final ValidationResult getValidationResult() {
|
||||
final CommandLineFile file = getCommandLineFile();
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
return file.getValidationResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return real command used in parent file (if any)
|
||||
*/
|
||||
@Nullable
|
||||
protected final Command getCommand() {
|
||||
final CommandLineFile file = getCommandLineFile();
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
return file.findRealCommand();
|
||||
}
|
||||
}
|
||||
@@ -13,25 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Visitor to process options and arguments
|
||||
* Command line element type to be used in parser
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see CommandLinePart#accept(CommandLinePartVisitor)
|
||||
*/
|
||||
public interface CommandLinePartVisitor {
|
||||
|
||||
/**
|
||||
* @param option option to visit
|
||||
*/
|
||||
void visitOption(@NotNull CommandLineOption option);
|
||||
|
||||
/**
|
||||
* @param argument argument to visit
|
||||
*/
|
||||
void visitArgument(@NotNull CommandLineArgument argument);
|
||||
final class CommandLineElementType extends IElementType {
|
||||
CommandLineElementType(@NotNull @NonNls final String debugName) {
|
||||
super(debugName, CommandLineLanguage.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Command line file type
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineFileType extends LanguageFileType {
|
||||
public static final FileType INSTANCE = new CommandLineFileType();
|
||||
/**
|
||||
* Command line extension
|
||||
*/
|
||||
static final String EXTENSION = "cmdline";
|
||||
|
||||
CommandLineFileType() {
|
||||
super(CommandLineLanguage.INSTANCE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return CommandLineLanguage.INSTANCE.getID();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return CommandLineLanguage.INSTANCE.getID();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDefaultExtension() {
|
||||
return '.' + EXTENSION;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -13,24 +13,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.intellij.openapi.fileTypes.FileTypeConsumer;
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Positional or option argument.
|
||||
* Factory to create command line files
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineArgument extends CommandLinePart {
|
||||
|
||||
public CommandLineArgument(@NotNull final WordWithPosition argumentValue) {
|
||||
super(argumentValue);
|
||||
}
|
||||
|
||||
|
||||
public final class CommandLineFileTypeFactory extends FileTypeFactory {
|
||||
@Override
|
||||
public void accept(@NotNull final CommandLinePartVisitor visitor) {
|
||||
visitor.visitArgument(this);
|
||||
public void createFileTypes(@NotNull final FileTypeConsumer consumer) {
|
||||
consumer.consume(CommandLineFileType.INSTANCE, CommandLineFileType.EXTENSION);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.*;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Inspection that ensures command line options and arguments are correct.
|
||||
* It works only if list of available commands is provided to command file
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineInspection extends LocalInspectionTool {
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return PyBundle.message("commandLine.inspection.name");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder,
|
||||
final boolean isOnTheFly,
|
||||
@NotNull final LocalInspectionToolSession session) {
|
||||
return new MyVisitor(holder);
|
||||
}
|
||||
|
||||
private static final class MyVisitor extends CommandLineVisitor {
|
||||
@NotNull
|
||||
private final ProblemsHolder myHolder;
|
||||
|
||||
private MyVisitor(@NotNull final ProblemsHolder holder) {
|
||||
myHolder = holder;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CommandLineFile getFile(@NotNull final PsiElement element) {
|
||||
return PsiTreeUtil.getParentOfType(element, CommandLineFile.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ValidationResult getValidationResult(@NotNull final PsiElement element) {
|
||||
final CommandLineFile file = getFile(element);
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
return file.getValidationResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCommand(@NotNull final CommandLineCommand o) {
|
||||
super.visitCommand(o);
|
||||
final CommandLineFile file = getFile(o);
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
final List<Command> commands = file.getCommands();
|
||||
if (commands == null) {
|
||||
return;
|
||||
}
|
||||
for (final Command command : commands) {
|
||||
if (o.getText().equals(command.getName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
myHolder.registerProblem(o, PyBundle.message("commandLine.inspection.badCommand"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOption(@NotNull final CommandLineOption o) {
|
||||
super.visitOption(o);
|
||||
final ValidationResult validationResult = getValidationResult(o);
|
||||
if (validationResult != null && validationResult.isBadValue(o)) {
|
||||
myHolder.registerProblem(o, PyBundle.message("commandLine.inspection.badOption"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitArgument(@NotNull final CommandLineArgument o) {
|
||||
super.visitArgument(o);
|
||||
final ValidationResult validationResult = getValidationResult(o);
|
||||
if (validationResult != null) {
|
||||
if (validationResult.isBadValue(o)) {
|
||||
myHolder.registerProblem(o, PyBundle.message("commandLine.inspection.badArgument"));
|
||||
}
|
||||
else if (validationResult.isExcessArgument(o)) {
|
||||
myHolder.registerProblem(o, PyBundle.message("commandLine.inspection.excessArgument"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,22 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.swingView;
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import com.intellij.lang.Language;
|
||||
|
||||
/**
|
||||
* "Execute command" action
|
||||
*
|
||||
* Command line language itself
|
||||
* <pre>my_command --option=value foo</pre>
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class ExecutionKeyStrokeAction extends KeyStrokeAction {
|
||||
ExecutionKeyStrokeAction() {
|
||||
super(KeyStrokeInfo.EXECUTION);
|
||||
}
|
||||
public final class CommandLineLanguage extends Language {
|
||||
public static final CommandLineLanguage INSTANCE = new CommandLineLanguage();
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
myPresenter.executionRequested();
|
||||
private CommandLineLanguage() {
|
||||
super("CommandLine");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineOption;
|
||||
import com.jetbrains.commandInterface.command.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Ref to be injected into command line option
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineOptionReference extends CommandLineElementReference<CommandLineOption> {
|
||||
CommandLineOptionReference(@NotNull final CommandLineOption element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
final ValidationResult validationResult = getValidationResult();
|
||||
if (validationResult == null) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
final Collection<String> result = new ArrayList<String>();
|
||||
|
||||
for (final Option option : validationResult.getUnusedOptions()) {
|
||||
// Suggest long options for -- and short for -
|
||||
result.addAll((getElement().isLong() ? option.getLongNames() : option.getShortNames()));
|
||||
}
|
||||
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.ParserDefinition;
|
||||
import com.intellij.lang.PsiParser;
|
||||
import com.intellij.lexer.FlexAdapter;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.FileViewProvider;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.tree.IFileElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes.Factory;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Command line language parser definition
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineParserDefinition implements ParserDefinition {
|
||||
@NotNull
|
||||
@Override
|
||||
public Lexer createLexer(final Project project) {
|
||||
return new FlexAdapter(new _CommandLineLexer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiParser createParser(final Project project) {
|
||||
return new CommandLineParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFileElementType getFileNodeType() {
|
||||
return new IFileElementType(CommandLineLanguage.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TokenSet getWhitespaceTokens() {
|
||||
return TokenSet.create(TokenType.WHITE_SPACE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TokenSet getCommentTokens() {
|
||||
return TokenSet.EMPTY;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TokenSet getStringLiteralElements() {
|
||||
return TokenSet.EMPTY;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement createElement(final ASTNode node) {
|
||||
return Factory.createElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiFile createFile(final FileViewProvider viewProvider) {
|
||||
return new CommandLineFile(viewProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpaceRequirements spaceExistanceTypeBetweenTokens(final ASTNode left, final ASTNode right) {
|
||||
return SpaceRequirements.MAY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.lang.PsiBuilder;
|
||||
import com.intellij.lang.parser.GeneratedParserUtilBase;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Tool to be used in parser generation to handle "=" for long option
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class CommandLineParserUtil extends GeneratedParserUtilBase {
|
||||
private CommandLineParserUtil() {
|
||||
}
|
||||
|
||||
static void bound_argument(@NotNull final PsiBuilder b, final int i) {
|
||||
final IElementType tokenType = b.getTokenType();
|
||||
final IElementType leftElement = b.rawLookup(-1);
|
||||
final IElementType rightElement = b.rawLookup(1);
|
||||
if (leftElement == null || TokenType.WHITE_SPACE.equals(leftElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* At '=' position: if no whitespace to left and right, we move to argument.
|
||||
* And we report error if whitespace to the left.
|
||||
*/
|
||||
if (tokenType == CommandLineElementTypes.EQ) {
|
||||
if (leftElement.equals(CommandLineElementTypes.LONG_OPTION_NAME_TOKEN)) {
|
||||
if (rightElement == null || TokenType.WHITE_SPACE.equals(rightElement)) {
|
||||
b.error("Space between argument is its value is unexpected");
|
||||
}
|
||||
b.advanceLexer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.patterns.PlatformPatterns;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Injects references to command-line parts
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineReferenceContributor extends PsiReferenceContributor {
|
||||
private static final ReferenceProvider REFERENCE_PROVIDER = new ReferenceProvider();
|
||||
|
||||
@Override
|
||||
public void registerReferenceProviders(@NotNull final PsiReferenceRegistrar registrar) {
|
||||
registrar.registerReferenceProvider(PlatformPatterns.psiElement(CommandLineElement.class), REFERENCE_PROVIDER);
|
||||
}
|
||||
|
||||
|
||||
private static class ReferenceProvider extends PsiReferenceProvider {
|
||||
@NotNull
|
||||
@Override
|
||||
public final PsiReference[] getReferencesByElement(@NotNull final PsiElement element,
|
||||
@NotNull final ProcessingContext context) {
|
||||
if (element instanceof CommandLineCommand) {
|
||||
return new PsiReference[]{new CommandLineCommandReference((CommandLineCommand)element)};
|
||||
}
|
||||
if (element instanceof CommandLineArgument) {
|
||||
return new PsiReference[]{new CommandLineArgumentReference((CommandLineArgument)element)};
|
||||
}
|
||||
if (element instanceof CommandLineOption) {
|
||||
return new PsiReference[]{new CommandLineOptionReference((CommandLineOption)element)};
|
||||
}
|
||||
return PsiReference.EMPTY_ARRAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.lexer.FlexAdapter;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
|
||||
import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Highlights tokens based on lexer
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
|
||||
@NotNull
|
||||
@Override
|
||||
public SyntaxHighlighter getSyntaxHighlighter(@Nullable final Project project,
|
||||
@Nullable final VirtualFile virtualFile) {
|
||||
return new CommandLineSyntaxHighlighter();
|
||||
}
|
||||
|
||||
private static class CommandLineSyntaxHighlighter implements SyntaxHighlighter {
|
||||
private static final Map<IElementType, TextAttributesKey> ATTRIBUTES = new HashMap<IElementType, TextAttributesKey>();
|
||||
public static final TextAttributesKey[] NO_ATTRS = new TextAttributesKey[0];
|
||||
|
||||
static {
|
||||
ATTRIBUTES.put(CommandLineElementTypes.LITERAL_STARTS_FROM_LETTER,
|
||||
TextAttributesKey.createTextAttributesKey("GNU.LETTER", DefaultLanguageHighlighterColors.LOCAL_VARIABLE)
|
||||
);
|
||||
ATTRIBUTES.put(CommandLineElementTypes.LITERAL_STARTS_FROM_DIGIT,
|
||||
TextAttributesKey.createTextAttributesKey("GNU.NUMBER", DefaultLanguageHighlighterColors.NUMBER)
|
||||
);
|
||||
ATTRIBUTES.put(CommandLineElementTypes.SHORT_OPTION_NAME_TOKEN,
|
||||
TextAttributesKey.createTextAttributesKey("GNU.SHORT_OPTION", DefaultLanguageHighlighterColors.INSTANCE_METHOD)
|
||||
|
||||
);
|
||||
ATTRIBUTES.put(CommandLineElementTypes.LONG_OPTION_NAME_TOKEN,
|
||||
TextAttributesKey.createTextAttributesKey("GNU.LONG_OPTION", DefaultLanguageHighlighterColors.INSTANCE_METHOD)
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Lexer getHighlightingLexer() {
|
||||
return new FlexAdapter(new _CommandLineLexer());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TextAttributesKey[] getTokenHighlights(final IElementType tokenType) {
|
||||
final TextAttributesKey attributesKey = ATTRIBUTES.get(tokenType);
|
||||
return (attributesKey == null ? NO_ATTRS : new TextAttributesKey[]{attributesKey});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.commandInterface.command.Option;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineArgument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Result of command line validation
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface ValidationResult {
|
||||
/**
|
||||
* @param element element to check
|
||||
* @return true if element is known to have bad/illegal value
|
||||
*/
|
||||
boolean isBadValue(@NotNull PsiElement element);
|
||||
|
||||
/**
|
||||
* @param argument element to check
|
||||
* @return true if argument is excess
|
||||
*/
|
||||
boolean isExcessArgument(@NotNull CommandLineArgument argument);
|
||||
|
||||
/**
|
||||
* @return list of allowed options unused by user
|
||||
*/
|
||||
@NotNull
|
||||
Collection<Option> getUnusedOptions();
|
||||
|
||||
/**
|
||||
* @param argument argument to check
|
||||
* @return true if argument is option argument (position argument otherwise)
|
||||
*/
|
||||
boolean isOptionArgument(@NotNull CommandLineArgument argument);
|
||||
|
||||
/**
|
||||
* @param argument argument to check
|
||||
* @return list of available argument values or null if unknown
|
||||
*/
|
||||
@Nullable
|
||||
Collection<String> getPossibleArgumentValues(@NotNull CommandLineArgument argument);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*TODO:Add generation to ant*/
|
||||
|
||||
// This file is used to auto-generate lexer and parser for gnu command line language
|
||||
// Please make sure .flex, and .java generated files are all in "gen" folder under this module.
|
||||
// Generate flex first, then generate lexer by flex, and then generate PSI and parser
|
||||
|
||||
{
|
||||
// All 3 elements should implement this class
|
||||
mixin("command")="com.jetbrains.commandInterface.gnuCommandLine.CommandLineElement"
|
||||
mixin("argument")="com.jetbrains.commandInterface.gnuCommandLine.CommandLineElement"
|
||||
mixin("option")="com.jetbrains.commandInterface.gnuCommandLine.CommandLineElement"
|
||||
|
||||
parserClass = 'com.jetbrains.commandInterface.gnuCommandLine.CommandLineParser'
|
||||
parserUtilClass="com.jetbrains.commandInterface.gnuCommandLine.CommandLineParserUtil"
|
||||
psiImplUtilClass="com.jetbrains.commandInterface.gnuCommandLine.psi.impl.CommandLinePsiImplUtils"
|
||||
psiPackage = 'com.jetbrains.commandInterface.gnuCommandLine.psi'
|
||||
psiImplPackage = 'com.jetbrains.commandInterface.gnuCommandLine.psi.impl'
|
||||
|
||||
elementTypeHolderClass = 'com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementTypes'
|
||||
psiClassPrefix = "CommandLine"
|
||||
psiVisitorName = "CommandLineVisitor"
|
||||
elementTypeClass = 'com.jetbrains.commandInterface.gnuCommandLine.CommandLineElementType'
|
||||
|
||||
|
||||
tokens=[
|
||||
space='regexp:\s+' // WARNING: Comment out or remove this (space) before generating Flex file! It is here only for live preview.
|
||||
EQ = '='
|
||||
LITERAL_STARTS_FROM_LETTER='regexp:\p{Alpha}(\w|:|\\|/|\.)*'
|
||||
LITERAL_STARTS_FROM_DIGIT='regexp:\p{Digit}(\w|:|\\\|/|\.)*'
|
||||
SHORT_OPTION_NAME_TOKEN='regexp:-\p{Alpha}'
|
||||
LONG_OPTION_NAME_TOKEN='regexp:--\p{Alpha}(-|\w)*'
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
root ::= command (argument | option ) * <<eof>>
|
||||
command ::= LITERAL_STARTS_FROM_LETTER
|
||||
option ::= (short_option_name <<bound_argument>> ? | long_option_name <<bound_argument>> ?) {
|
||||
methods=[ getOptionName isLong ]
|
||||
}
|
||||
private short_option_name ::= SHORT_OPTION_NAME_TOKEN
|
||||
private long_option_name ::= LONG_OPTION_NAME_TOKEN
|
||||
argument ::= LITERAL_STARTS_FROM_LETTER | LITERAL_STARTS_FROM_DIGIT
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <h1>GNU command line language PSI</h1>
|
||||
* <h2>Command line language</h2>
|
||||
* <p>
|
||||
* <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02">GNU</a> command line language syntax
|
||||
* based on <a href="http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html">POSIX</a> syntax.
|
||||
* <pre>
|
||||
* my_command --option --option-2=argument positional_argument -s
|
||||
* </pre>
|
||||
* </p>
|
||||
* <h2>PSI</h2>
|
||||
* <p>Generation based on Grammar-Kit, see .bnf file, do not edit parser nor lexer manually.
|
||||
* When parsed, {@link com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile} is root element for
|
||||
* {@link com.jetbrains.commandInterface.gnuCommandLine.CommandLineLanguage}.
|
||||
* <strong>Warning</strong>: always fill {@link com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile#setCommands(java.util.List)}
|
||||
* if possible.
|
||||
* </p>
|
||||
* <h2>Extension points</h2>
|
||||
* <p>This package has a a lot of extension points (language, inspection etc). Make sure all of them are registered</p>
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.commandInterface.gnuCommandLine;
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine.psi;
|
||||
|
||||
import com.intellij.extapi.psi.PsiFileBase;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.FileViewProvider;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.CommandLineLanguage;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.ValidationResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Gnu command line file (topmost element).
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineFile extends PsiFileBase {
|
||||
private static final Key<List<Command>> COMMANDS = Key.create("COMMANDS");
|
||||
|
||||
public CommandLineFile(final FileViewProvider provider) {
|
||||
super(provider, CommandLineLanguage.INSTANCE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileType getFileType() {
|
||||
|
||||
return getViewProvider().getVirtualFile().getFileType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of commands, available for this file
|
||||
* @see #setCommands(List)
|
||||
*/
|
||||
@Nullable
|
||||
public List<Command> getCommands() {
|
||||
return getCopyableUserData(COMMANDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param commands list of commands, available for this file. Better to provide one, if you know
|
||||
*/
|
||||
public void setCommands(@NotNull final List<Command> commands) {
|
||||
putCopyableUserData(COMMANDS, commands);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to find real command used in this file.
|
||||
* You need to first inject list of commands {@link #setCommands(List)}.
|
||||
*
|
||||
* @return Command if found and available, or null if command can't be parsed or bad command.
|
||||
*/
|
||||
@Nullable
|
||||
public Command findRealCommand() {
|
||||
final String command = getCommand();
|
||||
final List<Command> realCommands = getCommands();
|
||||
if (realCommands == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (final Command realCommand : realCommands) {
|
||||
if (realCommand.getName().equals(command)) {
|
||||
return realCommand;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to validate file.
|
||||
*
|
||||
* @return file validation info or null if file is junk or list of commands is unknown (see {@link #setCommands(List)})
|
||||
*/
|
||||
@Nullable
|
||||
public ValidationResult getValidationResult() {
|
||||
return ValidationResultImpl.create(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command (text) typed by user. I.e "my_command" in "my_command --foo --bar"
|
||||
*/
|
||||
@Nullable
|
||||
public String getCommand() {
|
||||
final CommandLineCommand command = PsiTreeUtil.getChildOfType(this, CommandLineCommand.class);
|
||||
if (command != null) {
|
||||
return command.getText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all arguments from file
|
||||
*/
|
||||
@NotNull
|
||||
public Collection<CommandLineArgument> getArguments() {
|
||||
return PsiTreeUtil.findChildrenOfType(this, CommandLineArgument.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all options from file
|
||||
*/
|
||||
@NotNull
|
||||
public Collection<CommandLineOption> getOptions() {
|
||||
return PsiTreeUtil.findChildrenOfType(this, CommandLineOption.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine.psi;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.ValidationResult;
|
||||
import com.jetbrains.commandInterface.command.Argument;
|
||||
import com.jetbrains.commandInterface.command.Command;
|
||||
import com.jetbrains.commandInterface.command.Option;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Validation result provider and holder implemented as visitor.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class ValidationResultImpl extends CommandLineVisitor implements ValidationResult {
|
||||
/**
|
||||
* List of options with names
|
||||
*/
|
||||
@NotNull
|
||||
private final HashMap<String, Option> myOptions = new HashMap<String, Option>();
|
||||
/**
|
||||
* We always need command to validate args
|
||||
*/
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
/**
|
||||
* Number of next positional argument. I.e. will be 3 for "my_arg arg_1 arg_2"
|
||||
*/
|
||||
private int myCurrentPositionArgument;
|
||||
/**
|
||||
* If next arg is supposed to be option arg, then option and number of expected args stored here.
|
||||
* Null stored otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
private Pair<Option, Integer> myCurrentOptionAndArgsLeft;
|
||||
/**
|
||||
* List of elements whose values are known to be bad
|
||||
*/
|
||||
@NotNull
|
||||
private final Collection<PsiElement> myBadValues = new ArrayList<PsiElement>();
|
||||
/**
|
||||
* List of elements which is known to be excess
|
||||
*/
|
||||
@NotNull
|
||||
private final Collection<CommandLineArgument> myExcessArguments = new ArrayList<CommandLineArgument>();
|
||||
/**
|
||||
* Possible values for argument
|
||||
*/
|
||||
@NotNull
|
||||
private final Map<CommandLineArgument, List<String>> myPossibleValues = new HashMap<CommandLineArgument, List<String>>();
|
||||
/**
|
||||
* List of arguments known to be option arguments
|
||||
*/
|
||||
@NotNull
|
||||
private final Collection<CommandLineArgument> myOptionArguments = new ArrayList<CommandLineArgument>();
|
||||
|
||||
private ValidationResultImpl(@NotNull final Command command) {
|
||||
for (final Option option : command.getOptions()) {
|
||||
for (final String optionName : option.getAllNames()) {
|
||||
myOptions.put(optionName, option);
|
||||
}
|
||||
}
|
||||
myCommand = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBadValue(@NotNull final PsiElement element) {
|
||||
return myBadValues.contains(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExcessArgument(@NotNull final CommandLineArgument argument) {
|
||||
return myExcessArguments.contains(argument);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Collection<Option> getUnusedOptions() {
|
||||
return myOptions.values();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isOptionArgument(@NotNull final CommandLineArgument argument) {
|
||||
return myOptionArguments.contains(argument);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Collection<String> getPossibleArgumentValues(@NotNull final CommandLineArgument argument) {
|
||||
return myPossibleValues.get(argument);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates validation result by file
|
||||
* @param file file to validate
|
||||
* @return validation result or null if file has no command or command is unknown
|
||||
*/
|
||||
@Nullable
|
||||
static ValidationResult create(final CommandLineFile file) {
|
||||
final Command command = file.findRealCommand();
|
||||
if (command == null) {
|
||||
return null;
|
||||
}
|
||||
final ValidationResultImpl validationLayout = new ValidationResultImpl(command);
|
||||
file.acceptChildren(validationLayout);
|
||||
return validationLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArgument(@NotNull final CommandLineArgument o) {
|
||||
super.visitArgument(o);
|
||||
if (myCurrentOptionAndArgsLeft != null) {
|
||||
processOptionArgument(o);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process as positional
|
||||
processPositionalArgument(o);
|
||||
}
|
||||
|
||||
private void processPositionalArgument(@NotNull final CommandLineArgument o) {
|
||||
final Pair<Boolean, Argument> argumentPair = myCommand.getArgumentsInfo().getArgument(myCurrentPositionArgument++);
|
||||
if (argumentPair == null) {
|
||||
myExcessArguments.add(o);
|
||||
}
|
||||
else {
|
||||
processArgument(o, argumentPair.second);
|
||||
}
|
||||
}
|
||||
|
||||
private void processOptionArgument(@NotNull final CommandLineArgument o) {
|
||||
assert myCurrentOptionAndArgsLeft != null: "Method can't be called if no current option exist";
|
||||
if (myCurrentOptionAndArgsLeft.second > 0) {
|
||||
myCurrentOptionAndArgsLeft = Pair.create(myCurrentOptionAndArgsLeft.first, myCurrentOptionAndArgsLeft.second - 1);
|
||||
final Pair<Integer, Argument> argumentAndQuantity = myCurrentOptionAndArgsLeft.first.getArgumentAndQuantity();
|
||||
// TODO: Use class instead of pair to prevent such a stupid checks
|
||||
assert argumentAndQuantity != null: "Option has arguments left but no argument info";
|
||||
final Argument argumentInfo = argumentAndQuantity.getSecond();
|
||||
processArgument(o, argumentInfo);
|
||||
|
||||
myOptionArguments.add(o);
|
||||
}
|
||||
else if (myCurrentOptionAndArgsLeft.second == 0) {
|
||||
myCurrentOptionAndArgsLeft = null;
|
||||
myExcessArguments.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
private void processArgument(@NotNull final CommandLineArgument o, final Argument argumentInfo) {
|
||||
final List<String> availableValues = argumentInfo.getAvailableValues();
|
||||
if (availableValues != null) {
|
||||
myPossibleValues.put(o, availableValues);
|
||||
}
|
||||
if (!argumentInfo.isValid(o.getText())) {
|
||||
myBadValues.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitWhiteSpace(final PsiWhiteSpace space) {
|
||||
super.visitWhiteSpace(space);
|
||||
// -aSHORT_OPT_ARGUMENT, but -a NEW_POSITION_ARGUMENT, so whitespace makes sense
|
||||
if (myCurrentOptionAndArgsLeft != null && myCurrentOptionAndArgsLeft.second == 0) {
|
||||
myCurrentOptionAndArgsLeft = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOption(@NotNull final CommandLineOption o) {
|
||||
super.visitOption(o);
|
||||
if (myOptions.containsKey(o.getOptionName())) {
|
||||
// Remove from list of available options
|
||||
final Option option = myOptions.remove(o.getOptionName());
|
||||
for (final String optionName : option.getAllNames()) {
|
||||
myOptions.remove(optionName);
|
||||
}
|
||||
|
||||
final Pair<Integer, Argument> argumentAndQuantity = option.getArgumentAndQuantity();
|
||||
if (argumentAndQuantity != null) {
|
||||
myCurrentOptionAndArgsLeft = Pair.create(option, argumentAndQuantity.first);
|
||||
}
|
||||
else {
|
||||
myCurrentOptionAndArgsLeft = new Pair<Option, Integer>(option, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
myBadValues.add(o); //No such option available
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine.psi.impl;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineOption;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Class to be used by autogenerated PSI elements to delegate logic
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class CommandLinePsiImplUtils {
|
||||
|
||||
|
||||
private CommandLinePsiImplUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if option is long (--long-option vs -s)
|
||||
*
|
||||
* @param o option
|
||||
* @return true if long
|
||||
*/
|
||||
static boolean isLong(@NotNull final CommandLineOption o) {
|
||||
return o.getLongOptionNameToken() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns option name regardless it is long or short.
|
||||
*
|
||||
* @param o option
|
||||
* @return name (if any)
|
||||
*/
|
||||
@Nullable
|
||||
@NonNls
|
||||
static String getOptionName(@NotNull final CommandLineOption o) {
|
||||
final PsiElement longNameToken = o.getLongOptionNameToken();
|
||||
if (longNameToken != null) {
|
||||
return longNameToken.getText();
|
||||
}
|
||||
final PsiElement shortOptionNameToken = o.getShortOptionNameToken();
|
||||
if (shortOptionNameToken != null) {
|
||||
return shortOptionNameToken.getText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Command with arguments and options.
|
||||
* Each {@link com.jetbrains.python.commandInterface.command.Command} may have one or more positional {@link com.jetbrains.python.commandInterface.command.Argument arguments}
|
||||
* and several {@link com.jetbrains.python.commandInterface.command.Option options}.
|
||||
* Gnu command line language PSI implementation.
|
||||
* Do not use this package directly, consider using interfaces from {@link com.jetbrains.commandInterface.gnuCommandLine.psi} instead.
|
||||
* <strong>Warning</strong>: most files in this package is autogenerated. Do not touch them directly.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi.impl;
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandLineParser.CommandLineParser} based on <a href="https://docs.python.org/2/library/optparse.html">OptParse</a>
|
||||
* See {@link com.jetbrains.python.commandLineParser.optParse.OptParseCommandLineParser}
|
||||
* Gnu command line public PSI interfaces: Command, option, argument etc.
|
||||
*
|
||||
* <strong>Warning</strong>: most files in this package is autogenerated. Do not touch them directly.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser.optParse;
|
||||
package com.jetbrains.commandInterface.gnuCommandLine.psi;
|
||||
55
python/src/com/jetbrains/commandInterface/package-info.java
Normal file
55
python/src/com/jetbrains/commandInterface/package-info.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <h1>Command-line interface</h1>
|
||||
* <h2>What is the purpose of this package?</h2>
|
||||
* <p>
|
||||
* This package is based on ideas of command-line with command, positional arguments, options and their arguments.
|
||||
* Initial idea is taken from <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02">POSIX</a>
|
||||
* and enhanced by <a href="http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html">GNU</a>.
|
||||
* It also supported by Python in <a href="https://docs.python.org/2/library/optparse.html">optparse</a> package.
|
||||
* Command-line is something like
|
||||
* <pre>my_command positional_argument -s --long-option --another-long-option=arg1 arg2 some_other_arg</pre>
|
||||
* and this package helps you to parse command lines, highlight errors, display a console-like interface and execute commands.
|
||||
* </p>
|
||||
* <h2>What this package consists of?</h2>
|
||||
* <p>
|
||||
* This package has 3 subpackages:
|
||||
* <ol>
|
||||
* <li>{@link com.jetbrains.commandInterface.command} contains classes to describe commands, arguments and options.
|
||||
* It is up to you where to obtain list of available commands, but you should implement {@link com.jetbrains.commandInterface.command.Command}
|
||||
* first, and create list of it with arguments and options. See package info for more details</li>
|
||||
* <li>{@link com.jetbrains.commandInterface.gnuCommandLine} is language based on
|
||||
* <a href="http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html">GNU</a> command line representation.
|
||||
* It has PSI, so it parses command lines into tree of PsiElements. But this package <strong>is not only for parsing</strong>:
|
||||
* If you provide list of
|
||||
* {@link com.jetbrains.commandInterface.command.Command commands} to {@link com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile}
|
||||
* (see {@link com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile#setCommands(java.util.List)}), it will inject references
|
||||
* (to provide autocompletion) and activate inspection to validate options and arguments. </li>
|
||||
* <li>{@link com.jetbrains.commandInterface.console} displays console-like interface at the bottom of the screen to give user
|
||||
* ability to wotk with your command-line.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
* <h2>How to use this package?</h2>
|
||||
* <ol>
|
||||
* <li>Implement {@link com.jetbrains.commandInterface.command.Command}</li>
|
||||
* <li>Create list of {@link com.jetbrains.commandInterface.command.Command commands}</li>
|
||||
* <li>Provide it to {@link com.jetbrains.commandInterface.console.CommandLineConsole#createConsole(com.intellij.openapi.module.Module, java.lang.String, java.util.List)}</li>
|
||||
* </ol>
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.commandInterface;
|
||||
@@ -864,16 +864,9 @@ remote.interpreter.configure.temp.files.path.label=PyCharm helpers path:
|
||||
custom.type.mimic.name=Dynamic class based on {0}
|
||||
|
||||
|
||||
# Values for command argument value validation
|
||||
commandLine.validation.badCommand=Unknown command
|
||||
commandLine.validation.argMissing=Required argument value is missing
|
||||
commandLine.validation.optArgMissing=Option argument value is missing
|
||||
commandLine.validation.argBadValue=Argument can't have this value
|
||||
commandLine.validation.excessArg=Excess argument value
|
||||
commandLine.validation.badOption=Bad option or option already set
|
||||
commandLine.validation.noArgAllowed=No argument allowed for this option
|
||||
# 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}"
|
||||
# CommandLine
|
||||
commandLine.inspection.name=Command-line inspection
|
||||
commandLine.inspection.badCommand=Bad or unknown command. make sure this command really exists.
|
||||
commandLine.inspection.badOption=Bad or unknown option. make sure this option really exists.
|
||||
commandLine.inspection.badArgument=Argument can't have this value. use autocompletion to check list of possible values.
|
||||
commandLine.inspection.excessArgument=Excess argument or argument is not possible here
|
||||
|
||||
@@ -1,158 +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;
|
||||
|
||||
import com.intellij.util.Range;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Word with range position. To be used to mark some part of text or split it.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class WordWithPosition extends Range<Integer> {
|
||||
@NotNull
|
||||
private final String myWord;
|
||||
|
||||
/**
|
||||
* Creates word with beam (it has start, but it is infinite)
|
||||
*
|
||||
* @param word word
|
||||
* @param from start
|
||||
*/
|
||||
public WordWithPosition(@NotNull final String word, final int from) {
|
||||
this(word, from, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param word text
|
||||
* @param from from where
|
||||
* @param to to where
|
||||
*/
|
||||
public WordWithPosition(@NotNull final String word, final int from, final int to) {
|
||||
super(from, to);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns texts only from collection of instances of this class
|
||||
*
|
||||
* @param words collection of instances of this class
|
||||
* @return collection of strings (text)
|
||||
*/
|
||||
@NotNull
|
||||
public static List<String> fetchText(@NotNull final Collection<WordWithPosition> words) {
|
||||
final List<String> result = new ArrayList<String>(words.size());
|
||||
for (final WordWithPosition word : words) {
|
||||
result.add(word.myWord);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new instance with all fields copied but text
|
||||
*
|
||||
* @param newText new test to add
|
||||
* @return new instance
|
||||
*/
|
||||
@NotNull
|
||||
public WordWithPosition copyWithDifferentText(@NotNull final String newText) {
|
||||
return new WordWithPosition(newText, getFrom(), getTo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WordWithPosition{" +
|
||||
"myWord='" + myWord + '\'' +
|
||||
", myFrom=" + getFrom() +
|
||||
", myTo=" + getTo() +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
WordWithPosition position = (WordWithPosition)o;
|
||||
|
||||
if (!getFrom().equals(position.getFrom())) return false;
|
||||
if (!getTo().equals(position.getTo())) return false;
|
||||
if (!myWord.equals(position.myWord)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myWord.hashCode();
|
||||
result = 31 * result + getFrom();
|
||||
result = 31 * result + getTo();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tool that splits text into parts using one or more whitespace as delimiter.
|
||||
* Each part contains text and boundaries (from and to)
|
||||
*
|
||||
* @param text text to split
|
||||
* @return parse result
|
||||
*/
|
||||
@NotNull
|
||||
public static List<WordWithPosition> splitText(@NotNull final String text) {
|
||||
// TODO: Rewrite using regex or scanner?
|
||||
int position = 0;
|
||||
int wordStart = -1;
|
||||
final List<WordWithPosition> parts = new ArrayList<WordWithPosition>();
|
||||
for (final char c : text.toCharArray()) {
|
||||
if (Character.isWhitespace(c) && wordStart != -1) {
|
||||
// Close word
|
||||
parts.add(new WordWithPosition(text.substring(wordStart, position), wordStart, position));
|
||||
wordStart = -1;
|
||||
}
|
||||
else if (!Character.isWhitespace(c) && wordStart == -1) {
|
||||
// Start word
|
||||
wordStart = position;
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
if (wordStart != -1) {
|
||||
// Adding last word
|
||||
parts.add(new WordWithPosition(text.substring(wordStart), wordStart, position));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +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;
|
||||
|
||||
import com.jetbrains.python.vp.Presenter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Presenter for command-line interface to be paired with view.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface CommandInterfacePresenter extends Presenter {
|
||||
/**
|
||||
* Called by view when user types new text or text changed by some other reason
|
||||
*
|
||||
*/
|
||||
void textChanged();
|
||||
|
||||
/**
|
||||
* Called by view when user requests for completion (like tab)
|
||||
*
|
||||
* @param valueFromSuggestionList value selected from suggestion list (if any selected)
|
||||
*/
|
||||
void completionRequested(@Nullable String valueFromSuggestionList);
|
||||
|
||||
/**
|
||||
* Called by view when user asks for suggestions (like CTRL+Space)
|
||||
*/
|
||||
void suggestionRequested();
|
||||
|
||||
/**
|
||||
* Called by view when user wants to execute command (like enter)
|
||||
*
|
||||
*/
|
||||
void executionRequested();
|
||||
}
|
||||
@@ -1,38 +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;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Adapter that holds view.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
|
||||
public abstract class CommandInterfacePresenterAdapter implements CommandInterfacePresenter {
|
||||
@NotNull
|
||||
protected final CommandInterfaceView myView;
|
||||
|
||||
protected CommandInterfacePresenterAdapter(@NotNull final CommandInterfaceView view) {
|
||||
myView = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
myView.show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +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;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* View for command-line interface to be paired with view.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void show();
|
||||
|
||||
/**
|
||||
* 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 caret
|
||||
* @param toSelect word to select in list (if any)
|
||||
*/
|
||||
void displaySuggestions(@NotNull SuggestionsBuilder suggestions, boolean absolute, @Nullable String toSelect);
|
||||
|
||||
|
||||
/**
|
||||
* 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 defaultSubText default text
|
||||
* @param suggestionAvailablePlaces list of places where suggestions are available in format [from, to].
|
||||
*/
|
||||
void configureSubTexts(@Nullable String defaultSubText,
|
||||
@NotNull List<Range<Integer>> suggestionAvailablePlaces);
|
||||
|
||||
/**
|
||||
* Hide suggestion list
|
||||
*/
|
||||
void removeSuggestions();
|
||||
|
||||
|
||||
/**
|
||||
* @return text, entered by user
|
||||
*/
|
||||
@NotNull
|
||||
String getText();
|
||||
|
||||
|
||||
/**
|
||||
* 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 errors places to be marked as errors with error text.
|
||||
* @param infoBalloons places to display info balloon
|
||||
* @see #AFTER_LAST_CHARACTER_RANGE
|
||||
*/
|
||||
void setInfoAndErrors(@NotNull final Collection<WordWithPosition> infoBalloons, @NotNull final Collection<WordWithPosition> errors);
|
||||
|
||||
|
||||
/**
|
||||
* Inserts text after caret moving next chars to the right
|
||||
*
|
||||
* @param text text to insert
|
||||
*/
|
||||
void insertTextAfterCaret(@NotNull String text);
|
||||
|
||||
/**
|
||||
* Replaces current text with another one.
|
||||
*
|
||||
* @param from from
|
||||
* @param to to
|
||||
* @param newText text to replace
|
||||
*/
|
||||
void replaceText(final int from, final int to, @NotNull String newText);
|
||||
|
||||
/**
|
||||
* @return position of caret (in chars)
|
||||
*/
|
||||
int getCaretPosition();
|
||||
}
|
||||
@@ -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.command;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: Support regex validation as well
|
||||
|
||||
/**
|
||||
* Command <strong>positional, not named</strong> argument (not option!).
|
||||
* This class represents command argument, not its value.
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class Argument {
|
||||
/**
|
||||
* Argument help user-readable text
|
||||
*/
|
||||
@NotNull
|
||||
private final String myHelpText;
|
||||
/**
|
||||
* List of values argument may have. Null if any value is possible.
|
||||
*/
|
||||
@Nullable
|
||||
private final List<String> myAvailableValues;
|
||||
|
||||
|
||||
/**
|
||||
* @param helpText Argument help user-readable text
|
||||
*/
|
||||
public Argument(@NotNull final String helpText) {
|
||||
this(helpText, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param helpText Argument help user-readable text
|
||||
* @param availableValues List of values argument may have. Null if any value is possible.
|
||||
*/
|
||||
public Argument(@NotNull final String helpText, @Nullable final List<String> availableValues) {
|
||||
myHelpText = helpText;
|
||||
myAvailableValues = (availableValues == null ? null : new ArrayList<String>(availableValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Argument help user-readable text
|
||||
*/
|
||||
@NotNull
|
||||
public String getHelpText() {
|
||||
return myHelpText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of values argument may have. Null if any value is possible.
|
||||
*/
|
||||
@Nullable
|
||||
public List<String> getAvailableValues() {
|
||||
return (myAvailableValues == null ? null : Collections.unmodifiableList(myAvailableValues));
|
||||
}
|
||||
}
|
||||
@@ -1,42 +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.command;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information about option argument
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface OptionArgumentInfo {
|
||||
/**
|
||||
* Validates argument value
|
||||
*
|
||||
* @param value value to validate
|
||||
* @return true if valid
|
||||
*/
|
||||
boolean isValid(@NotNull String value);
|
||||
|
||||
/**
|
||||
* @return list of available values (if argument is based on list of choices), or null if any value is accepted (but should be validated)
|
||||
*/
|
||||
@Nullable
|
||||
List<String> getAvailableValues();
|
||||
}
|
||||
@@ -1,51 +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.command;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* For options, whose argument is based on list of choices
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class OptionChoiceBasedArgumentInfo implements OptionArgumentInfo {
|
||||
@NotNull
|
||||
private final List<String> myChoices = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* @param choices available choices
|
||||
*/
|
||||
public OptionChoiceBasedArgumentInfo(@NotNull final Collection<String> choices) {
|
||||
myChoices.addAll(choices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(@NotNull final String value) {
|
||||
return myChoices.contains(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getAvailableValues() {
|
||||
return Collections.unmodifiableList(myChoices);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +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.command;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* For options, whose argument is based on certain type.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see OptionArgumentType
|
||||
*/
|
||||
public final class OptionTypedArgumentInfo implements OptionArgumentInfo {
|
||||
@NotNull
|
||||
private final OptionArgumentType myType;
|
||||
|
||||
/**
|
||||
* @param type type argument(s) of this option may have
|
||||
*/
|
||||
public OptionTypedArgumentInfo(@NotNull final OptionArgumentType type) {
|
||||
myType = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(@NotNull final String value) {
|
||||
// We only check integer for now
|
||||
if (myType == OptionArgumentType.INTEGER) {
|
||||
try {
|
||||
// We just getCommandLineInfo it to get exception
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
Integer.parseInt(value);
|
||||
}
|
||||
catch (final NumberFormatException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getAvailableValues() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,204 +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.commandBasedRangeDriver;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.commandInterface.command.Argument;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import com.jetbrains.python.commandInterface.command.OptionArgumentInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.Executor;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.SuggestionInfo;
|
||||
import com.jetbrains.python.commandLineParser.CommandLine;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParser;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePart;
|
||||
import com.jetbrains.python.commandLineParser.MalformedCommandLineException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Driver that returns pack of range infos for certain command line
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandBasedRangeInfoDriver implements RangeInfoDriver {
|
||||
@NotNull
|
||||
private final Map<String, Command> myCommands = new TreeMap<String, Command>(); // To sort commands by name
|
||||
@NotNull
|
||||
private final Module myModule;
|
||||
@NotNull
|
||||
private final CommandLineParser myCommandLineParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param module module (to be used in execution)
|
||||
* @param commandLineParser parser to parse command lines
|
||||
* @param commands available commands
|
||||
*/
|
||||
public CommandBasedRangeInfoDriver(
|
||||
@NotNull final Module module,
|
||||
@NotNull final CommandLineParser commandLineParser,
|
||||
@NotNull final Collection<? extends Command> commands) {
|
||||
for (final Command command : commands) {
|
||||
myCommands.put(command.getName(), command);
|
||||
}
|
||||
myModule = module;
|
||||
myCommandLineParser = commandLineParser;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Pair<Executor, List<RangeInfo>> getCommandLineInfo(@NotNull final String commandLineText) {
|
||||
// TODO: Copty/paste with exceptiojn
|
||||
if (StringUtil.isEmpty(commandLineText)) {
|
||||
final RangeInfo info = new RangeInfo(null, "", new SuggestionInfo(
|
||||
true, true, myCommands.keySet()
|
||||
), RangeInfo.TERMINATION_RANGE, false
|
||||
);
|
||||
return new Pair<Executor, List<RangeInfo>>(null, Collections.singletonList(info));
|
||||
}
|
||||
final CommandLine commandLine;
|
||||
try {
|
||||
commandLine = myCommandLineParser.parse(commandLineText);
|
||||
}
|
||||
catch (final MalformedCommandLineException ignored) {
|
||||
return new Pair<Executor, List<RangeInfo>>(null, Collections.singletonList(
|
||||
new RangeInfo(null, PyBundle.message("commandLine.validation.badCommand"), new SuggestionInfo(
|
||||
false, true, myCommands.keySet()
|
||||
), new Range<Integer>(0, commandLineText.length()), false)
|
||||
));
|
||||
}
|
||||
|
||||
final Command command = getExistingCommand(commandLine);
|
||||
final List<CommandLinePart> commandLineParts = commandLine.getParts();
|
||||
if (command == null) {
|
||||
// Bad command inserted
|
||||
return createBadCommandInfo(commandLine);
|
||||
}
|
||||
|
||||
final UnusedOptionsCollector unusedOptionsCollector = UnusedOptionsCollector.create(command, commandLineParts);
|
||||
final RangeInfoCollector rangeInfoCollector = RangeInfoCollector.create(command, commandLineParts, unusedOptionsCollector);
|
||||
|
||||
|
||||
|
||||
final SuggestionInfo commandSuggestions = new SuggestionInfo(false, true, myCommands.keySet());
|
||||
// Add command as first range info
|
||||
final List<RangeInfo> rangeInfos =
|
||||
new ArrayList<RangeInfo>(Collections.singletonList(new RangeInfo(null, null, commandSuggestions, commandLine.getCommand(), false)));
|
||||
// Then add collected infos
|
||||
rangeInfos.addAll(rangeInfoCollector.getRangeInfos());
|
||||
|
||||
|
||||
/////// What about "after the caret" ?
|
||||
final Pair<Boolean, Argument> unsatisfiedArgument = rangeInfoCollector.getUnsatisfiedPositionalArgument();
|
||||
final OptionArgumentInfo unsatisfiedOptionArgument = rangeInfoCollector.getUnsatisfiedOptionArgument();
|
||||
|
||||
|
||||
// TODO: Move to collector after test
|
||||
if (unsatisfiedOptionArgument != null) {
|
||||
final List<String> availableValues = unsatisfiedOptionArgument.getAvailableValues();
|
||||
final SuggestionInfo suggestionInfo;
|
||||
if (availableValues != null) {
|
||||
|
||||
suggestionInfo =
|
||||
new SuggestionInfo(false, false, availableValues);
|
||||
}
|
||||
else {
|
||||
suggestionInfo = null;
|
||||
}
|
||||
rangeInfos
|
||||
.add(new RangeInfo(null, PyBundle.message("commandLine.validation.optArgMissing"), suggestionInfo, RangeInfo.TERMINATION_RANGE,
|
||||
false));
|
||||
}
|
||||
else if (unsatisfiedArgument != null) {
|
||||
final boolean required = unsatisfiedArgument.first;
|
||||
final Argument argument = unsatisfiedArgument.second;
|
||||
// Only add error if required
|
||||
final String error = required ? PyBundle.message("commandLine.validation.argMissing") : null;
|
||||
final List<String> availableValues = unusedOptionsCollector.addUnusedOptions(argument.getAvailableValues());
|
||||
final RangeInfo lastArgInfo =
|
||||
new RangeInfo(argument.getHelpText(), error,
|
||||
(availableValues != null ? new SuggestionInfo(false, false, availableValues) : null), RangeInfo.TERMINATION_RANGE,
|
||||
false);
|
||||
rangeInfos.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 range info will always be used, even 200 chars after last place
|
||||
rangeInfos.add(new RangeInfo(null, null, rangeInfoCollector.getCurrentSuggestions(false, null), RangeInfo.TERMINATION_RANGE, false));
|
||||
}
|
||||
|
||||
assert rangeInfos.size() >= commandLineParts.size() : "Contract broken: not enough chunks";
|
||||
return Pair.<Executor, List<RangeInfo>>create(new CommandExecutor(command, myModule, commandLine.getPartsAsText()), rangeInfos);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Command getExistingCommand(@NotNull final CommandLine commandLine) {
|
||||
return myCommands.get(commandLine.getCommand().getText());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates range info info signaling command is bad or junk
|
||||
*
|
||||
*
|
||||
* @param commandLine command line passed by user
|
||||
* @return info to return
|
||||
*/
|
||||
@NotNull
|
||||
private Pair<Executor, List<RangeInfo>> createBadCommandInfo(final CommandLine commandLine) {
|
||||
final List<RangeInfo> result = new ArrayList<RangeInfo>();
|
||||
// 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
|
||||
result
|
||||
.add(new RangeInfo(null, PyBundle.message("commandLine.validation.badCommand"), new SuggestionInfo(true, true, myCommands.keySet()),
|
||||
commandLine.getCommand(), false));
|
||||
// Command is unknown, all other parts are junk
|
||||
for (final CommandLinePart part : commandLine.getParts()) {
|
||||
result.add(new RangeInfo(null, "", false, part.getWord()));
|
||||
}
|
||||
|
||||
|
||||
return Pair.create(null, result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds option by its name
|
||||
* @param command current command
|
||||
* @param optionName option name
|
||||
* @return option or null if no option found
|
||||
*/
|
||||
@Nullable
|
||||
static Option findOptionByName(@NotNull final Command command, @NotNull final String optionName) {
|
||||
for (final Option option : command.getOptions()) {
|
||||
for (final String name : option.getAllNames()) {
|
||||
if (name.equals(optionName)) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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.commandBasedRangeDriver;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.Executor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Executes commands according to {@link Executor} contract
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class CommandExecutor implements Executor {
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
@NotNull
|
||||
private final Module myModule;
|
||||
@NotNull
|
||||
private final String[] myArguments;
|
||||
|
||||
/**
|
||||
* @param command command to execute
|
||||
* @param module module to execute against
|
||||
* @param argumentsLine all command arguments as testline
|
||||
*/
|
||||
CommandExecutor(@NotNull final Command command, @NotNull final Module module, @NotNull final String argumentsLine) {
|
||||
myCommand = command;
|
||||
myModule = module;
|
||||
myArguments = (StringUtil.isEmpty(argumentsLine) ? ArrayUtil.EMPTY_STRING_ARRAY : argumentsLine.split(" "));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getExecutionDescription() {
|
||||
return myCommand.getHelp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
myCommand.execute(myModule, Arrays.asList(myArguments));
|
||||
}
|
||||
}
|
||||
@@ -1,250 +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.commandBasedRangeDriver;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.command.Argument;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import com.jetbrains.python.commandInterface.command.OptionArgumentInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.SuggestionInfo;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineArgument;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineOption;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePart;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePartVisitor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Visitor that collections ranges info by visiting options and arguments
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class RangeInfoCollector implements CommandLinePartVisitor {
|
||||
/**
|
||||
* Options already met in command line. It has nothing to do with {@link UnusedOptionsCollector}!
|
||||
*/
|
||||
@NotNull
|
||||
private final Set<String> myUsedOptions = new HashSet<String>();
|
||||
@NotNull
|
||||
private final List<RangeInfo> myRangeInfos = new ArrayList<RangeInfo>();
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
|
||||
private int myNumOfProcessedPositionalArguments = 0;
|
||||
private boolean mySkipNextArgument;
|
||||
private Pair<Integer, OptionArgumentInfo> myExpectedOptionArgument;
|
||||
@NotNull
|
||||
private final UnusedOptionsCollector myUnusedOptionsCollector;
|
||||
|
||||
/**
|
||||
* @param command command
|
||||
* @param unusedOptionsCollector instance of unused options collector
|
||||
*/
|
||||
private RangeInfoCollector(@NotNull final Command command,
|
||||
@NotNull final UnusedOptionsCollector unusedOptionsCollector) {
|
||||
myCommand = command;
|
||||
myUnusedOptionsCollector = unusedOptionsCollector;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static RangeInfoCollector create(@NotNull final Command command,
|
||||
@NotNull final Iterable<CommandLinePart> commandLineParts,
|
||||
@NotNull final UnusedOptionsCollector unusedOptionsCollector) {
|
||||
final RangeInfoCollector infoCollector = new RangeInfoCollector(command, unusedOptionsCollector);
|
||||
for (final CommandLinePart part : commandLineParts) {
|
||||
part.accept(infoCollector);
|
||||
}
|
||||
return infoCollector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOption(@NotNull final CommandLineOption option) {
|
||||
if (processOptionArgument(option.getWord())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final Option commandOption = CommandBasedRangeInfoDriver.findOptionByName(myCommand, option.getOptionName());
|
||||
final WordWithPosition attachedArgument = option.getAttachedArgument();
|
||||
if (commandOption == null) {
|
||||
// There is no such option
|
||||
// To Mark attached arg so we skip it in {@link #visitArgument}
|
||||
if (attachedArgument != null) {
|
||||
mySkipNextArgument = true;
|
||||
}
|
||||
// No such option
|
||||
myRangeInfos.add(new RangeInfo(null, PyBundle.message("commandLine.validation.badOption"), getCurrentSuggestions(false, null),
|
||||
option.getWord(), false));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final Pair<Integer, OptionArgumentInfo> argumentAndQuantity = commandOption.getArgumentAndQuantity();
|
||||
|
||||
|
||||
// If option already used, then mark it
|
||||
final String optionAlreadyUsed = Sets.intersection(Sets.newHashSet(commandOption.getAllNames()), myUsedOptions).isEmpty()
|
||||
? null
|
||||
: PyBundle.message("commandLine.validation.badOption");
|
||||
myRangeInfos
|
||||
.add(new RangeInfo(commandOption.getHelp(), optionAlreadyUsed, getCurrentSuggestions(false, null), option.getWord(),
|
||||
argumentAndQuantity != null));
|
||||
|
||||
// remove from existing options
|
||||
myUsedOptions.addAll(commandOption.getAllNames());
|
||||
|
||||
|
||||
if (argumentAndQuantity == null) {
|
||||
if (attachedArgument != null) {
|
||||
myRangeInfos.add(new RangeInfo(null, PyBundle.message("commandLine.validation.noArgAllowed"), false, attachedArgument));
|
||||
mySkipNextArgument = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Some option args required
|
||||
myExpectedOptionArgument = argumentAndQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process option argument (not to be confused with positional!)
|
||||
*
|
||||
* @param currentPart part with argument.
|
||||
* @return false if there should not be any option argument so this method did nothing. True if there should be and it is processed.
|
||||
*/
|
||||
private boolean processOptionArgument(@NotNull final WordWithPosition currentPart) {
|
||||
if (myExpectedOptionArgument == null) {
|
||||
return false;
|
||||
}
|
||||
final OptionArgumentInfo argumentInfo = myExpectedOptionArgument.second;
|
||||
int argsLeft = myExpectedOptionArgument.first;
|
||||
|
||||
final boolean valid = argumentInfo.isValid(currentPart.getText());
|
||||
final List<String> availableValues = argumentInfo.getAvailableValues();
|
||||
final SuggestionInfo suggestions;
|
||||
if (availableValues != null) {
|
||||
suggestions = new SuggestionInfo(false, false, availableValues);
|
||||
}
|
||||
else {
|
||||
suggestions = null;
|
||||
}
|
||||
|
||||
myRangeInfos.add(new RangeInfo(null, (valid ? null : PyBundle.message("commandLine.validation.argBadValue")),
|
||||
suggestions, currentPart, false));
|
||||
|
||||
|
||||
if (--argsLeft == 0) {
|
||||
myExpectedOptionArgument = null;
|
||||
}
|
||||
else {
|
||||
myExpectedOptionArgument = Pair.create(argsLeft, argumentInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns suggestions available for current position.
|
||||
*
|
||||
* @param showAutomatically make suggestions displayed automatically
|
||||
* @param argumentPair argument to use. If null, current argument according to internal counter would be used.
|
||||
* @return suggestiom info or null if no suggestion available
|
||||
*/
|
||||
@Nullable
|
||||
SuggestionInfo getCurrentSuggestions(final boolean showAutomatically, @Nullable Pair<Boolean, Argument> argumentPair) {
|
||||
if (argumentPair == null) {
|
||||
argumentPair = myCommand.getArgumentsInfo().getArgument(myNumOfProcessedPositionalArguments);
|
||||
}
|
||||
|
||||
final List<String> suggestions = argumentPair != null ? myUnusedOptionsCollector.addUnusedOptions(
|
||||
argumentPair.second.getAvailableValues()) : myUnusedOptionsCollector.addUnusedOptions(
|
||||
null);
|
||||
if (suggestions == null) {
|
||||
return null;
|
||||
}
|
||||
return new SuggestionInfo(showAutomatically, false, suggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArgument(@NotNull final CommandLineArgument argument) {
|
||||
if (mySkipNextArgument) {
|
||||
// Skip argument, clear flag and do nothing
|
||||
mySkipNextArgument = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (processOptionArgument(argument.getWord())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Pair<Boolean, Argument> argumentPair = myCommand.getArgumentsInfo().getArgument(myNumOfProcessedPositionalArguments++);
|
||||
if (argumentPair == null) {
|
||||
//Exceed!
|
||||
myRangeInfos.add(new RangeInfo(null, PyBundle.message("commandLine.validation.excessArg"), false, argument.getWord()));
|
||||
return;
|
||||
}
|
||||
final Argument commandArgument = argumentPair.second;
|
||||
|
||||
final List<String> argumentAvailableValues = commandArgument.getAvailableValues();
|
||||
final String argumentValue = argument.getWord().getText();
|
||||
String errorMessage = null;
|
||||
if (argumentAvailableValues != null && !argumentAvailableValues.contains(argumentValue)) {
|
||||
// Bad value
|
||||
errorMessage = PyBundle.message("commandLine.validation.argBadValue");
|
||||
}
|
||||
// Argument seems to be ok. We suggest values automatically only if value is bad
|
||||
myRangeInfos.add(new RangeInfo(commandArgument.getHelpText(), errorMessage,
|
||||
getCurrentSuggestions(errorMessage != null, argumentPair),
|
||||
argument.getWord(), false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return calculates range infos for all command line parts (not the command itself!)
|
||||
*/
|
||||
@NotNull
|
||||
Collection<RangeInfo> getRangeInfos() {
|
||||
return Collections.unmodifiableList(myRangeInfos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Unsatisfied (currently expected) positional argument ([required, arg]) or null if no arg expected
|
||||
*/
|
||||
@Nullable
|
||||
Pair<Boolean, Argument> getUnsatisfiedPositionalArgument() {
|
||||
return myCommand.getArgumentsInfo().getArgument(myNumOfProcessedPositionalArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Unsatisfied (currently expected) option argument or null if no option argument expected
|
||||
*/
|
||||
public OptionArgumentInfo getUnsatisfiedOptionArgument() {
|
||||
if (myExpectedOptionArgument != null) {
|
||||
return myExpectedOptionArgument.second;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +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.commandBasedRangeDriver;
|
||||
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineArgument;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineOption;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePart;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePartVisitor;
|
||||
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.Set;
|
||||
|
||||
/**
|
||||
* Collections all used and unused options to report unused options then.
|
||||
* It may be used to add unused options to suggestion list {@link #addUnusedOptions(Collection)}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class UnusedOptionsCollector implements CommandLinePartVisitor {
|
||||
@NotNull
|
||||
private final Set<String> myUnusedOptions = new HashSet<String>();
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
|
||||
/**
|
||||
* @param command command to be used
|
||||
* @param commandLineParts command line parts
|
||||
* @return instance
|
||||
*/
|
||||
static UnusedOptionsCollector create(@NotNull final Command command, @NotNull final Iterable<CommandLinePart> commandLineParts) {
|
||||
final UnusedOptionsCollector collector = new UnusedOptionsCollector(command);
|
||||
for (final CommandLinePart part : commandLineParts) {
|
||||
part.accept(collector);
|
||||
}
|
||||
return collector;
|
||||
}
|
||||
|
||||
private UnusedOptionsCollector(@NotNull final Command command) {
|
||||
for (final Option option : command.getOptions()) {
|
||||
myUnusedOptions.addAll(option.getAllNames());
|
||||
}
|
||||
myCommand = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOption(@NotNull final CommandLineOption option) {
|
||||
final Option commandOption = CommandBasedRangeInfoDriver.findOptionByName(myCommand, option.getOptionName());
|
||||
if (commandOption != null) {
|
||||
myUnusedOptions.removeAll(commandOption.getAllNames());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges list of unused options and other values provided as argument.
|
||||
*
|
||||
* @param mainValues values to merge options with. May be null.
|
||||
* @return null of no options and no values provided or merged list of values and options. See method usages for more info
|
||||
*/
|
||||
@Nullable
|
||||
List<String> addUnusedOptions(@Nullable final Collection<String> mainValues) {
|
||||
if (mainValues == null && myUnusedOptions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final List<String> result = new ArrayList<String>(myUnusedOptions);
|
||||
|
||||
if (mainValues != null) {
|
||||
result.addAll(mainValues);
|
||||
}
|
||||
return (result.isEmpty() ? null : result);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitArgument(@NotNull final CommandLineArgument argument) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,29 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver} implementation based on idea of
|
||||
* {@link com.jetbrains.python.commandInterface.command command, option and argument}.
|
||||
*
|
||||
* See {@link com.jetbrains.python.commandInterface.commandBasedRangeDriver.CommandBasedRangeInfoDriver} as entry point.
|
||||
* It parses command line using {@link com.jetbrains.python.commandLineParser.CommandLineParser} and finds matching command and arguments
|
||||
* provided by user
|
||||
*
|
||||
* @see com.jetbrains.python.commandInterface.command
|
||||
* @see com.jetbrains.python.commandInterface.command.Command
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandBasedRangeDriver;
|
||||
@@ -1,46 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* View/Presenter pair that implements so-called "command line interface".
|
||||
* It has several abilities, including (but not limited):
|
||||
* <ol>
|
||||
* <li>Suggestion box</li>
|
||||
* <li>Error emphasising</li>
|
||||
* <li>Popups</li>
|
||||
* <li>AutoCompletion</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* System consists of {@link com.jetbrains.python.commandInterface.CommandInterfacePresenter}
|
||||
* and appropriate {@link com.jetbrains.python.commandInterface.CommandInterfaceView}.
|
||||
* See {@link com.jetbrains.python.vp} for more info.
|
||||
* </p>
|
||||
*
|
||||
* <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.rangeBasedPresenter}
|
||||
* </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
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface;
|
||||
@@ -1,246 +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.rangeBasedPresenter;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
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.*;
|
||||
|
||||
|
||||
/**
|
||||
* Presenter that uses {@link RangeInfoDriver} to obtain {@link RangeInfo info}.
|
||||
* Such info tells presenter whether this text has info, error, suggestions and so on.
|
||||
* If caret situated far from text, then next neariest range should be found (see {@link #findNearestRangeInfo()}.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see RangeInfo
|
||||
*/
|
||||
public final class RangeBasedPresenter extends CommandInterfacePresenterAdapter {
|
||||
@NotNull
|
||||
private final RangeInfoDriver myRangeInfoDriver;
|
||||
@Nullable
|
||||
private Executor myExecutor;
|
||||
private final SortedSet<RangeInfo> myRangeInfos = new TreeSet<RangeInfo>();
|
||||
|
||||
public RangeBasedPresenter(@NotNull final CommandInterfaceView view,
|
||||
@NotNull final RangeInfoDriver rangeInfoDriver) {
|
||||
super(view);
|
||||
myRangeInfoDriver = rangeInfoDriver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
super.launch();
|
||||
reparseText(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textChanged() {
|
||||
reparseText(false);
|
||||
}
|
||||
|
||||
private void reparseText(final boolean skipSuggestions) {
|
||||
|
||||
|
||||
final Pair<Executor, List<RangeInfo>> rangeInfoStructure = myRangeInfoDriver.getCommandLineInfo(myView.getText());
|
||||
myExecutor = rangeInfoStructure.first;
|
||||
final List<RangeInfo> rangeInfos = rangeInfoStructure.second;
|
||||
|
||||
assert !rangeInfos.isEmpty() : "At least one chunk info should exist";
|
||||
myRangeInfos.clear();
|
||||
myRangeInfos.addAll(rangeInfos);
|
||||
|
||||
|
||||
// configure Errors And Balloons
|
||||
|
||||
final Collection<WordWithPosition> infoBalloons = new ArrayList<WordWithPosition>();
|
||||
final Collection<WordWithPosition> errorBalloons = new ArrayList<WordWithPosition>();
|
||||
|
||||
|
||||
for (final RangeInfo rangeInfo : rangeInfos) {
|
||||
final String error = rangeInfo.getError();
|
||||
if (error != null) {
|
||||
errorBalloons.add(new WordWithPosition(error, rangeInfo));
|
||||
}
|
||||
final String info = rangeInfo.getInfoBalloon();
|
||||
if (info != null) {
|
||||
infoBalloons.add(new WordWithPosition(info, rangeInfo));
|
||||
}
|
||||
}
|
||||
|
||||
myView.setInfoAndErrors(infoBalloons, errorBalloons);
|
||||
|
||||
|
||||
if (!skipSuggestions) {
|
||||
configureSuggestion(false);
|
||||
}
|
||||
|
||||
// Configure subtexts
|
||||
final List<Range<Integer>> placesWhereSuggestionAvailable = new ArrayList<Range<Integer>>();
|
||||
for (final RangeInfo rangeInfo : rangeInfos) {
|
||||
// If some place has suggestions, then add it
|
||||
if (rangeInfo.getSuggestions() != null) {
|
||||
placesWhereSuggestionAvailable.add(rangeInfo);
|
||||
}
|
||||
}
|
||||
final String statusText = (myExecutor != null ? myExecutor.getExecutionDescription() : null);
|
||||
myView.configureSubTexts(statusText, 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 RangeInfo rangeInfo = findNearestRangeInfo();
|
||||
final String text = getTextByRange(rangeInfo);
|
||||
|
||||
final SuggestionInfo suggestionInfo = rangeInfo.getSuggestions();
|
||||
if (suggestionInfo == null || (!suggestionInfo.isShowSuggestionsAutomatically() && !requestedExplicitly)) {
|
||||
return;
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (text != null && !requestedExplicitly) {
|
||||
filterLeaveOnlyMatching(suggestions, text);
|
||||
}
|
||||
// TODO: Place to add history
|
||||
if (!suggestions.isEmpty()) {
|
||||
// No need to display empty suggestions
|
||||
myView
|
||||
.displaySuggestions(new SuggestionsBuilder(suggestions), suggestionInfo.isShowAbsolute(), text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 range to use.
|
||||
*
|
||||
* @return nearest range info
|
||||
*/
|
||||
@NotNull
|
||||
private RangeInfo findNearestRangeInfo() {
|
||||
final int caretPosition = myView.getCaretPosition();
|
||||
|
||||
for (final RangeInfo range : myRangeInfos) {
|
||||
if (range.isWithin(caretPosition)) {
|
||||
return range;
|
||||
}
|
||||
if (range.getFrom() > caretPosition) {
|
||||
return range; // Ranges are sorted, so we are on the next range. Take it, if caret is not within range
|
||||
}
|
||||
}
|
||||
|
||||
return myRangeInfos.last();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void completionRequested(@Nullable final String valueFromSuggestionList) {
|
||||
final RangeInfo rangeInfo = findNearestRangeInfo();
|
||||
final String text = getTextByRange(rangeInfo);
|
||||
|
||||
if (valueFromSuggestionList != null) {
|
||||
// Just insert it
|
||||
if (text != null) { // If caret is on the text itself
|
||||
// TODO: Replace next range, if it has no space before it(--a=12 should be replaced wth arg)
|
||||
myView.replaceText(rangeInfo.getFrom(), rangeInfo.getTo(), valueFromSuggestionList);
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(valueFromSuggestionList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//User did not provide text no insert, do our best to find one
|
||||
|
||||
final SuggestionInfo suggestionInfo = rangeInfo.getSuggestions();
|
||||
if (suggestionInfo == null) {
|
||||
return; // No suggestion available for this chunk
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (text != null) {
|
||||
filterLeaveOnlyMatching(suggestions, text);
|
||||
}
|
||||
if (suggestions.size() == 1) {
|
||||
// Exclusive!
|
||||
if (text != null) {
|
||||
// TODO: Replace next range, if it has no space before it(--a=12 should be replaced wth arg)
|
||||
myView.replaceText(rangeInfo.getFrom(), rangeInfo.getTo(), suggestions.get(0));
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(suggestions.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for text under the range
|
||||
* @param rangeInfo range
|
||||
* @return text or null if range does not contain any text
|
||||
*/
|
||||
@Nullable
|
||||
private String getTextByRange(@NotNull final RangeInfo rangeInfo) {
|
||||
|
||||
if (rangeInfo.isTerminationRange()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
final String viewText = myView.getText();
|
||||
return viewText.substring(rangeInfo.getFrom(), rangeInfo.getTo());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executionRequested() {
|
||||
if (myExecutor == null) {
|
||||
// TODO: Display error somehow
|
||||
}
|
||||
else {
|
||||
myExecutor.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +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.rangeBasedPresenter;
|
||||
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Information about certain place in text, provided by driver.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class RangeInfo extends Range<Integer> implements Comparable<RangeInfo> {
|
||||
|
||||
|
||||
/**
|
||||
* Special "terminator": the last range which is after all other ranges.
|
||||
*/
|
||||
public static final Range<Integer> TERMINATION_RANGE = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE;
|
||||
|
||||
@Nullable
|
||||
private final String myInfoBalloon;
|
||||
@Nullable
|
||||
private final String myError;
|
||||
@Nullable
|
||||
private final SuggestionInfo mySuggestions;
|
||||
private final boolean myExclusiveBorders;
|
||||
|
||||
|
||||
/**
|
||||
* @param infoBalloon info to bind to this range
|
||||
* @param error error to bind to this range
|
||||
* @param exclusiveBorders true if range has exclusive borders and right border is not part of it. I.e. 3 is part of 1-3 range with out
|
||||
* of exclusive borders, but not part of exclusive range
|
||||
* @param range from and to
|
||||
*/
|
||||
public RangeInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error,
|
||||
final boolean exclusiveBorders,
|
||||
@NotNull final Range<Integer> range) {
|
||||
this(infoBalloon, error, null, range, exclusiveBorders);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 RangeInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error,
|
||||
@Nullable final SuggestionInfo suggestions,
|
||||
@NotNull final Range<Integer> range,
|
||||
final boolean exclusiveBorders) {
|
||||
super(range.getFrom(), range.getTo());
|
||||
myInfoBalloon = infoBalloon;
|
||||
myError = error;
|
||||
mySuggestions = suggestions;
|
||||
myExclusiveBorders = exclusiveBorders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(RangeInfo o) {
|
||||
return getFrom().compareTo(o.getFrom());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final Integer object) {
|
||||
if (!super.isWithin(object)) {
|
||||
return false;
|
||||
}
|
||||
if (myExclusiveBorders) {
|
||||
return object < getTo();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isTerminationRange() {
|
||||
// TODO: copy/paste with view
|
||||
return TERMINATION_RANGE.getFrom().equals(getFrom()) && TERMINATION_RANGE.getTo().equals(getTo());
|
||||
}
|
||||
}
|
||||
@@ -1,39 +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.rangeBasedPresenter;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Driver that knows how to getCommandLineInfo pack of chunks into chunk info.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface RangeInfoDriver {
|
||||
/**
|
||||
* Parses command line text into executor and pack of range infos.
|
||||
* There <strong>always</strong> should be at least one range info and the last one is almost always {@link RangeInfo#TERMINATION_RANGE).
|
||||
*
|
||||
* @param commandLineText command line text to parse.
|
||||
* @return pair or executor (the one that knows how to execute command line) and pack of range infos.
|
||||
* <strong>Warning! </strong> :Executor could be null if command can't be executed
|
||||
*/
|
||||
@NotNull
|
||||
Pair<Executor, List<RangeInfo>> getCommandLineInfo(@NotNull String commandLineText);
|
||||
}
|
||||
@@ -1,79 +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.rangeBasedPresenter;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SuggestionInfo{" +
|
||||
"mySuggestions=" + mySuggestions +
|
||||
", myShowSuggestionsAutomatically=" + myShowSuggestionsAutomatically +
|
||||
", myShowAbsolute=" + myShowAbsolute +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandInterface.CommandInterfacePresenter} implementation based on ideas of <strong>range info</strong>
|
||||
* and {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver}.
|
||||
* This presenter passes command line to driver, and it returns pack of range information.
|
||||
* Each record contains everything presenter needs to know about certain range (i.e. error between 2 and 5 chars).
|
||||
* Special type of range {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfo#TERMINATION_RANGE} exists, that
|
||||
* you may need to check.
|
||||
* <p/>
|
||||
* Presenter uses this information to display chunks correctly using view.
|
||||
* To use this package, {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver} should be implemented.
|
||||
* <p/>
|
||||
* See {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeBasedPresenter} as entry point
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.commandInterface.swingView.CommandInterfaceViewSwingImpl">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="4" left="4" bottom="4" right="2"/>
|
||||
<constraints>
|
||||
<xy x="1" y="67" width="404" height="245"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<hspacer id="91552">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
|
||||
<minimum-size width="-1" height="5"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<component id="4067e" class="javax.swing.JLabel" binding="myLabel">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Label"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="30372" class="com.jetbrains.python.commandInterface.swingView.SmartTextField" binding="myMainTextField" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<enabled value="true"/>
|
||||
<font/>
|
||||
</properties>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
<hspacer id="4b381">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<component id="7a794" class="javax.swing.JLabel" binding="mySubLabel">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Label"/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="c568">
|
||||
<constraints>
|
||||
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -1,434 +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.swingView;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||
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.suggestionList.SuggestionList;
|
||||
import com.jetbrains.python.suggestionList.SuggestionsBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
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.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.
|
||||
* 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
|
||||
*/
|
||||
@NotNull
|
||||
private final JBPopup myMainPopUp;
|
||||
private JPanel myPanel;
|
||||
/**
|
||||
* Upper label
|
||||
*/
|
||||
private JLabel myLabel;
|
||||
/**
|
||||
* Text field
|
||||
*/
|
||||
private SmartTextField myMainTextField;
|
||||
/**
|
||||
* 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 mySuggestionList;
|
||||
/**
|
||||
* Displayed when there is no text
|
||||
*/
|
||||
@Nullable
|
||||
private final String myPlaceHolderText;
|
||||
|
||||
/**
|
||||
* Information balloons that should be displayed when caret meets their boundaries.
|
||||
*/
|
||||
@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
|
||||
* @param title view title to display
|
||||
* @param project project
|
||||
* @param placeholderText text for placeholder (to be displayed when there is not text)
|
||||
*/
|
||||
public CommandInterfaceViewSwingImpl(@NotNull final CommandInterfacePresenter presenter,
|
||||
@NotNull final String title,
|
||||
@NotNull final Project project,
|
||||
@Nullable final String placeholderText) {
|
||||
myPresenter = presenter;
|
||||
myLabel.setText(title);
|
||||
myPlaceHolderText = placeholderText;
|
||||
|
||||
myMainPopUp = JBPopupFactory.getInstance().createComponentPopupBuilder(myPanel, myMainTextField)
|
||||
.setFocusable(true)
|
||||
.setRequestFocus(true)
|
||||
.createPopup();
|
||||
myMainTextField.setRequestFocusEnabled(true);
|
||||
myMainTextField.setFocusable(true);
|
||||
|
||||
|
||||
final int windowWidth = FileEditorManagerEx.getInstanceEx(project).getComponent().getRootPane().getWidth() - 10; // Little gap
|
||||
|
||||
myMainTextField
|
||||
.setPreferredWidthInPx(windowWidth);
|
||||
mySuggestionList = new SuggestionList(new MySuggestionListListener());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
myMainTextField.getDocument().addDocumentListener(this);
|
||||
myMainTextField.addCaretListener(this);
|
||||
myMainPopUp.addListener(this);
|
||||
if (myPlaceHolderText != null) {
|
||||
myMainTextField.setWaterMarkPlaceHolderText(myPlaceHolderText);
|
||||
}
|
||||
|
||||
|
||||
myMainTextField.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusLost(final FocusEvent e) {
|
||||
super.focusLost(e);
|
||||
myMainPopUp.cancel();
|
||||
}
|
||||
});
|
||||
myMainTextField.setFocusTraversalKeysEnabled(false);
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void displaySuggestions(@NotNull final SuggestionsBuilder suggestions, final boolean absolute, @Nullable final String toSelect) {
|
||||
|
||||
int left = 0;
|
||||
// Display text right after caret
|
||||
if (!absolute) {
|
||||
left = myMainTextField.getTextCaretPositionInPx();
|
||||
}
|
||||
mySuggestionList.showSuggestions(suggestions, new RelativePoint(myPanel, new Point(left, myPanel.getHeight())), toSelect);
|
||||
configureAppropriateStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(final LightweightWindowEvent event) {
|
||||
super.onClosed(event);
|
||||
mySuggestionList.close();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void insertUpdate(final DocumentEvent e) {
|
||||
processDocumentChange();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removeUpdate(final DocumentEvent e) {
|
||||
processDocumentChange();
|
||||
}
|
||||
|
||||
private void processDocumentChange() {
|
||||
myMainTextField.hideUnderline();
|
||||
myPresenter.textChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(final DocumentEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSuggestions() {
|
||||
mySuggestionList.close();
|
||||
configureAppropriateStatus();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void caretUpdate(final CaretEvent e) {
|
||||
|
||||
|
||||
// When caret moved, we need to check if balloon has to be displayed
|
||||
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;
|
||||
}
|
||||
|
||||
// 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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())) {
|
||||
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.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 void setInfoAndErrors(@NotNull final Collection<WordWithPosition> infoBalloons,
|
||||
@NotNull final Collection<WordWithPosition> errors) {
|
||||
synchronized (myInfoBalloons) {
|
||||
myInfoBalloons.clear();
|
||||
myInfoBalloons.addAll(infoBalloons);
|
||||
}
|
||||
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 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 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
|
||||
*/
|
||||
private class MyKeyListener extends KeyAdapter {
|
||||
@Override
|
||||
public void keyPressed(final KeyEvent e) {
|
||||
super.keyPressed(e);
|
||||
|
||||
final int keyCode = e.getKeyCode();
|
||||
if (keyCode == KeyEvent.VK_UP) {
|
||||
mySuggestionList.moveSelection(true);
|
||||
}
|
||||
else if (keyCode == KeyEvent.VK_DOWN) {
|
||||
mySuggestionList.moveSelection(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return myMainTextField.getText();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listener for suggestion list
|
||||
*/
|
||||
private class MySuggestionListListener extends JBPopupAdapter {
|
||||
|
||||
@Override
|
||||
public void onClosed(final LightweightWindowEvent event) {
|
||||
super.onClosed(event);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +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.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());
|
||||
}
|
||||
}
|
||||
@@ -1,72 +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.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;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +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.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());
|
||||
}
|
||||
}
|
||||
@@ -1,170 +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.swingView;
|
||||
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
import com.intellij.util.Range;
|
||||
import com.intellij.util.ui.StatusText;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Text field that has width to be changed and accepts error underline
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
@SuppressWarnings({"NonSerializableFieldInSerializableClass", "SerializableHasSerializationMethods"}) // Will never serialize it
|
||||
public class SmartTextField extends JTextField {
|
||||
/**
|
||||
* Placeholder for this textbox
|
||||
*/
|
||||
private StatusText myPlaceHolder;
|
||||
/**
|
||||
* Error (underline) info to display. Check {@link UnderlineInfo} class for more info
|
||||
*/
|
||||
@NotNull
|
||||
private final Collection<UnderlineInfo> myUnderlineInfo = new ArrayList<UnderlineInfo>();
|
||||
private int myPreferredWidth;
|
||||
|
||||
public SmartTextField() {
|
||||
setFont(EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.CONSOLE_PLAIN));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(final Graphics g) {
|
||||
super.paintComponent(g);
|
||||
if (myPlaceHolder != null) {
|
||||
myPlaceHolder.paint(this, g);
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Underlines certain place
|
||||
*
|
||||
* @param g canvas
|
||||
* @param from from where (int px)
|
||||
* @param to to where (int px)
|
||||
*/
|
||||
private void underline(@NotNull final Graphics g, final int from, final int to) {
|
||||
final int verticalPosition = getHeight() - 5;
|
||||
g.drawLine(from + getColumnWidth(), verticalPosition, to + getColumnWidth(), verticalPosition);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return place (in px) where caret.
|
||||
*/
|
||||
int getTextCaretPositionInPx() {
|
||||
return (getCaretPosition() + 1) * getColumnWidth();
|
||||
}
|
||||
|
||||
void setWaterMarkPlaceHolderText(@NotNull final String watermark) {
|
||||
myPlaceHolder = new MyStatusText(this);
|
||||
myPlaceHolder.setText(watermark);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
final Dimension dimension = super.getPreferredSize();
|
||||
final int placeHolderTextLength = ((myPlaceHolder != null) ? myPlaceHolder.getText().length() : 0);
|
||||
final int columns = Math.max(Math.max(getText().length(), placeHolderTextLength), getColumns());
|
||||
final int desiredSize = columns * getColumnWidth();
|
||||
return new Dimension(Math.max(Math.max(myPreferredWidth, dimension.width), desiredSize), dimension.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display underline
|
||||
*
|
||||
* @param color color to underline
|
||||
* @param from from (in chars)
|
||||
* @param to (in chars)
|
||||
*/
|
||||
final void underlineText(@NotNull final Color color, final int from, final int to) {
|
||||
final int columnWidth = getColumnWidth();
|
||||
synchronized (myUnderlineInfo) {
|
||||
myUnderlineInfo.add(new UnderlineInfo(from * columnWidth, to * columnWidth, color));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes underline
|
||||
*/
|
||||
void hideUnderline() {
|
||||
synchronized (myUnderlineInfo) {
|
||||
myUnderlineInfo.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets appropriate width in pixels
|
||||
*
|
||||
* @param width width in px
|
||||
*/
|
||||
void setPreferredWidthInPx(final int width) {
|
||||
myPreferredWidth = width;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to display placeholder
|
||||
*/
|
||||
private class MyStatusText extends StatusText {
|
||||
MyStatusText(final JComponent owner) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isStatusVisible() {
|
||||
return SmartTextField.this.getText().isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about underline
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
private static final class UnderlineInfo extends Range<Integer> {
|
||||
/**
|
||||
* Color to use to underline
|
||||
*/
|
||||
@NotNull
|
||||
private final Color myColor;
|
||||
|
||||
/**
|
||||
* @param from underline from where (in px)
|
||||
* @param to underline to where (in px)
|
||||
* @param color color to use to underline
|
||||
*/
|
||||
UnderlineInfo(final int from, final int to, @NotNull final Color color) {
|
||||
super(from, to);
|
||||
myColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +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.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();
|
||||
}
|
||||
}
|
||||
@@ -1,21 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Command-line like interface view GUI. See {@link com.jetbrains.python.commandInterface.swingView.CommandInterfaceViewSwingImpl}
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.swingView;
|
||||
@@ -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.commandLineParser;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
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 getCommandLineInfo 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 CommandLine {
|
||||
@NotNull
|
||||
private final List<CommandLinePart> myParts = new ArrayList<CommandLinePart>();
|
||||
@NotNull
|
||||
private final WordWithPosition myCommand;
|
||||
|
||||
public CommandLine(
|
||||
@NotNull final WordWithPosition command,
|
||||
@NotNull final Collection<CommandLinePart> parts) {
|
||||
myCommand = command;
|
||||
myParts.addAll(parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return command (i.e. "startapp" in "startapp my_app")
|
||||
*/
|
||||
@NotNull
|
||||
public WordWithPosition getCommand() {
|
||||
return myCommand;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<CommandLinePart> getParts() {
|
||||
return Collections.unmodifiableList(myParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all command parts as text (actually by bindning them back together)
|
||||
*/
|
||||
@NotNull
|
||||
public String getPartsAsText() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
int lastPosition = 0;
|
||||
for (final CommandLinePart part : getParts()) {
|
||||
final WordWithPosition partWord = part.getWord();
|
||||
if (lastPosition != partWord.getFrom()) {
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append(partWord.getText());
|
||||
lastPosition = partWord.getTo();
|
||||
}
|
||||
return StringUtil.trim(builder.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,69 +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.commandLineParser;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Option. Each option has name and may also have attached argument like "--long-option=attached_arg" or "-sATTACHED_ARG"
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineOption extends CommandLinePart {
|
||||
|
||||
@NotNull
|
||||
private final String myOptionName;
|
||||
|
||||
@Nullable
|
||||
private final WordWithPosition myAttachedArgument;
|
||||
|
||||
/**
|
||||
* @param option option (text and position)
|
||||
* @param optionName option name (like "--foo")
|
||||
* @param attachedArgument option attached argument (like --foo=ATTACHED_ARG)
|
||||
*/
|
||||
public CommandLineOption(@NotNull final WordWithPosition option,
|
||||
@NotNull final String optionName,
|
||||
@Nullable final WordWithPosition attachedArgument) {
|
||||
super(option);
|
||||
myOptionName = optionName;
|
||||
myAttachedArgument = attachedArgument;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return option name (like "--foo")
|
||||
*/
|
||||
@NotNull
|
||||
public String getOptionName() {
|
||||
return myOptionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return option attached argument (like --foo=ATTACHED_ARG)
|
||||
*/
|
||||
@Nullable
|
||||
public WordWithPosition getAttachedArgument() {
|
||||
return myAttachedArgument;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(@NotNull final CommandLinePartVisitor visitor) {
|
||||
visitor.visitOption(this);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +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.commandLineParser;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Engine to {@link CommandLine} structure from text.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface CommandLineParser {
|
||||
/**
|
||||
*
|
||||
* @param commandLineText command line to parse
|
||||
* @return command line information
|
||||
* @throws MalformedCommandLineException in case of bad commandline
|
||||
*/
|
||||
@NotNull
|
||||
CommandLine parse(@NotNull String commandLineText) throws MalformedCommandLineException;
|
||||
}
|
||||
@@ -1,51 +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.commandLineParser;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Part of command line. Known subclasses are {@link CommandLineOption} and {@link CommandLineArgument}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see CommandLineArgument
|
||||
* @see CommandLineOption
|
||||
*/
|
||||
public abstract class CommandLinePart {
|
||||
@NotNull
|
||||
private final WordWithPosition myWord;
|
||||
|
||||
/**
|
||||
* @param word word (and its position) this part represents
|
||||
*/
|
||||
protected CommandLinePart(@NotNull final WordWithPosition word) {
|
||||
myWord = word;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return word (and its position) this part represents
|
||||
*/
|
||||
@NotNull
|
||||
public final WordWithPosition getWord() {
|
||||
return myWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param visitor visitor to accept
|
||||
*/
|
||||
public abstract void accept(@NotNull CommandLinePartVisitor visitor);
|
||||
}
|
||||
@@ -1,110 +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.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandLineParser.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <a href="https://docs.python.org/2/library/optparse.html">Optparse</a>-based commandline parses.
|
||||
* According to optparse manual, commandline should look like:
|
||||
* <pre>command arg1 arg2 --long-bool-opt -s --another-opt opt_arg1 opt_arg2 --yet-another-opt=opt_arg3 arg4 </pre>.
|
||||
* You should understand difference between argument, long option, short option, and option argument before using this class.
|
||||
* It is documented in optparse manual.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class provides not only args and options (like many other parsers do), but also <strong>position in command line</strong>
|
||||
* which may be useful when you want to mark argument somehow.
|
||||
* </p>
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class OptParseCommandLineParser implements CommandLineParser {
|
||||
/**
|
||||
* Supported option parsers (option types, actually)
|
||||
* Short (-o) and long (--option) strategies are used here
|
||||
*/
|
||||
|
||||
private static final OptionParser[] OPTION_PARSERS = {new ShortOptionParser(), new LongOptionParser()};
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandLine parse(@NotNull final String commandLineText) throws MalformedCommandLineException {
|
||||
final Deque<WordWithPosition> parts = new ArrayDeque<WordWithPosition>(WordWithPosition.splitText(commandLineText));
|
||||
if (parts.isEmpty()) {
|
||||
throw new MalformedCommandLineException("No command provided");
|
||||
}
|
||||
final WordWithPosition command = parts.pop();
|
||||
|
||||
final List<CommandLinePart> optionsAndArguments = new ArrayList<CommandLinePart>();
|
||||
|
||||
for (final WordWithPosition part : parts) {
|
||||
final Pair<String, String> optionTextAndName = findOptionTextAndName(part.getText());
|
||||
if (optionTextAndName != null) {
|
||||
final String optionText = optionTextAndName.first;
|
||||
final String optionName = optionTextAndName.second;
|
||||
final WordWithPosition option = new WordWithPosition(optionText, part.getFrom(), part.getFrom() + optionText.length());
|
||||
|
||||
final WordWithPosition optionArgument;
|
||||
if (optionText.length() == part.getText().length()) {
|
||||
optionArgument = null;
|
||||
}
|
||||
else {
|
||||
// Looks like we have option argument here.
|
||||
final String argumentText = part.getText().substring(optionText.length());
|
||||
optionArgument = new WordWithPosition(argumentText, option.getTo(), part.getTo());
|
||||
}
|
||||
optionsAndArguments.add(new CommandLineOption(option, optionName, optionArgument)); //Option
|
||||
if (optionArgument != null) {
|
||||
optionsAndArguments.add(new CommandLineArgument(optionArgument)); // And its argument
|
||||
}
|
||||
}
|
||||
else {
|
||||
optionsAndArguments.add(new CommandLineArgument(part)); //Not an option. Should be argument
|
||||
}
|
||||
}
|
||||
return new CommandLine(command, optionsAndArguments);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse out option and its value iterating through the parsers
|
||||
*
|
||||
* @param text text believed to be an option like "--foo=123"
|
||||
* @return [option_text, option_name] or null of no such option
|
||||
* @see OptionParser#findOptionTextAndName(String)
|
||||
*/
|
||||
@Nullable
|
||||
private static Pair<String, String> findOptionTextAndName(@NotNull final String optionText) {
|
||||
for (final OptionParser parser : OPTION_PARSERS) {
|
||||
final Pair<String, String> optionTextAndName = parser.findOptionTextAndName(optionText);
|
||||
if (optionTextAndName != null) {
|
||||
return optionTextAndName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +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.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Engine that knows how to deal with option of certain style (like long and short)
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
interface OptionParser {
|
||||
/**
|
||||
* @param optionText text to parse (like --foo=bar)
|
||||
* @return null if option can't be parsed. Otherwise pair of [option_text, option_name]. That may match each other in some cases.
|
||||
*/
|
||||
@Nullable
|
||||
Pair<String, String> findOptionTextAndName(@NotNull String optionText);
|
||||
}
|
||||
@@ -1,60 +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.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Parses options using regex and template method
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
abstract class OptionParserRegexBased implements OptionParser {
|
||||
@NotNull
|
||||
private final Pattern myPattern;
|
||||
|
||||
/**
|
||||
* @param pattern regex. If option matches pattern, ({@link Matcher#find() opened matcher}) would be passed to template method
|
||||
* {@link #getOptionTextAndNameFromMatcher(Matcher)}
|
||||
*/
|
||||
protected OptionParserRegexBased(@NotNull final Pattern pattern) {
|
||||
myPattern = pattern;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final Pair<String, String> findOptionTextAndName(@NotNull final String optionText) {
|
||||
final Matcher matcher = myPattern.matcher(optionText);
|
||||
if (!matcher.find()) {
|
||||
return null;
|
||||
}
|
||||
return getOptionTextAndNameFromMatcher(matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains [option_text, option_name] from matcher
|
||||
*
|
||||
* @param matcher opened (with find() called) matcher
|
||||
* @return pair [option_text, option_name]
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract Pair<String, String> getOptionTextAndNameFromMatcher(@NotNull Matcher matcher);
|
||||
}
|
||||
@@ -1,29 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Engine to parse commandline from string to {@link com.jetbrains.python.commandLineParser.CommandLine} structure.
|
||||
*
|
||||
* Command line consists of command itself, {@link com.jetbrains.python.commandLineParser.CommandLineArgument arguments}
|
||||
* and {@link com.jetbrains.python.commandLineParser.CommandLineOption options}.
|
||||
* Use need to pass text to {@link com.jetbrains.python.commandLineParser.CommandLineParser parser} and obtain {@link com.jetbrains.python.commandLineParser.CommandLine}.
|
||||
* <p/>
|
||||
* 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;
|
||||
@@ -0,0 +1 @@
|
||||
<warning descr="Bad or unknown command. make sure this command really exists.">bad_command</warning> positional_argument --available-option=available_argument
|
||||
@@ -0,0 +1 @@
|
||||
command <warning descr="Argument can't have this value. use autocompletion to check list of possible values.">bad_positional_value</warning> --available-option=<warning descr="Argument can't have this value. use autocompletion to check list of possible values.">bad_argument</warning> <warning descr="Bad or unknown option. make sure this option really exists.">--bad-option</warning> <warning descr="Excess argument or argument is not possible here">unexpected_argument</warning> --option-no-argument=<warning descr="Excess argument or argument is not possible here">asd</warning>
|
||||
1
python/testData/commandLine/commandLine.cmdline
Normal file
1
python/testData/commandLine/commandLine.cmdline
Normal file
@@ -0,0 +1 @@
|
||||
command_name positional_argument_1 positional_argument_2 -sSMALL_OPTION_VALUE -a --long-option --long-option-2=long_option_value --long-option-3 argument_for_long_option
|
||||
32
python/testData/commandLine/commandLine.txt
Normal file
32
python/testData/commandLine/commandLine.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
FILE
|
||||
CommandLineCommandImpl(COMMAND)
|
||||
PsiElement(LITERAL_STARTS_FROM_LETTER)('command_name')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineArgumentImpl(ARGUMENT)
|
||||
PsiElement(LITERAL_STARTS_FROM_LETTER)('positional_argument_1')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineArgumentImpl(ARGUMENT)
|
||||
PsiElement(LITERAL_STARTS_FROM_LETTER)('positional_argument_2')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineOptionImpl(OPTION)
|
||||
PsiElement(SHORT_OPTION_NAME_TOKEN)('-s')
|
||||
CommandLineArgumentImpl(ARGUMENT)
|
||||
PsiElement(LITERAL_STARTS_FROM_LETTER)('SMALL_OPTION_VALUE')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineOptionImpl(OPTION)
|
||||
PsiElement(SHORT_OPTION_NAME_TOKEN)('-a')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineOptionImpl(OPTION)
|
||||
PsiElement(LONG_OPTION_NAME_TOKEN)('--long-option')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineOptionImpl(OPTION)
|
||||
PsiElement(LONG_OPTION_NAME_TOKEN)('--long-option-2')
|
||||
PsiElement(=)('=')
|
||||
CommandLineArgumentImpl(ARGUMENT)
|
||||
PsiElement(LITERAL_STARTS_FROM_LETTER)('long_option_value')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineOptionImpl(OPTION)
|
||||
PsiElement(LONG_OPTION_NAME_TOKEN)('--long-option-3')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineArgumentImpl(ARGUMENT)
|
||||
PsiElement(LITERAL_STARTS_FROM_LETTER)('argument_for_long_option')
|
||||
1
python/testData/commandLine/goodCommandLine.cmdline
Normal file
1
python/testData/commandLine/goodCommandLine.cmdline
Normal file
@@ -0,0 +1 @@
|
||||
command positional_argument --available-option=available_argument
|
||||
1
python/testData/commandLine/junk.cmdline
Normal file
1
python/testData/commandLine/junk.cmdline
Normal file
@@ -0,0 +1 @@
|
||||
--can_t_be
|
||||
3
python/testData/commandLine/junk.txt
Normal file
3
python/testData/commandLine/junk.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
FILE
|
||||
PsiErrorElement:LITERAL_STARTS_FROM_LETTER expected, got '--can_t_be'
|
||||
PsiElement(LONG_OPTION_NAME_TOKEN)('--can_t_be')
|
||||
1
python/testData/commandLine/optionNoValueJunk.cmdline
Normal file
1
python/testData/commandLine/optionNoValueJunk.cmdline
Normal file
@@ -0,0 +1 @@
|
||||
command --bad_value=
|
||||
9
python/testData/commandLine/optionNoValueJunk.txt
Normal file
9
python/testData/commandLine/optionNoValueJunk.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
FILE
|
||||
CommandLineCommandImpl(COMMAND)
|
||||
PsiElement(LITERAL_STARTS_FROM_LETTER)('command')
|
||||
PsiWhiteSpace(' ')
|
||||
CommandLineOptionImpl(OPTION)
|
||||
PsiElement(LONG_OPTION_NAME_TOKEN)('--bad_value')
|
||||
PsiErrorElement:Space between argument is its value is unexpected
|
||||
<empty list>
|
||||
PsiElement(=)('=')
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import org.junit.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Tests command line inspection
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineInspectionTest extends PyTestCase {
|
||||
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
CommandTestTools.initFileType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything should be ok
|
||||
*/
|
||||
public void testGoodCommandLine() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* No command provided in file
|
||||
*/
|
||||
public void testBadCommandLineNoCommand() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Command provided, but args and opts have errors
|
||||
*/
|
||||
public void testBadCommandLineWithCommand() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return CommandTestTools.TEST_PATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables inspection on testName.cmdline and checks it.
|
||||
*/
|
||||
private void doTest() {
|
||||
final PsiFile file = myFixture.configureByFile(getTestName(true) + '.' + CommandLineFileType.EXTENSION);
|
||||
Assert.assertSame("Bad file type!", CommandLineFile.class, file.getClass());
|
||||
final CommandLineFile commandLineFile = (CommandLineFile)file;
|
||||
commandLineFile.setCommands(CommandTestTools.createCommands());
|
||||
myFixture.enableInspections(CommandLineInspection.class);
|
||||
myFixture.checkHighlighting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.commandInterface.gnuCommandLine;
|
||||
|
||||
|
||||
import com.intellij.testFramework.ParsingTestCase;
|
||||
|
||||
import com.jetbrains.python.PythonTestUtil;
|
||||
|
||||
|
||||
/**
|
||||
* Tests command line parser
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineParserTest extends ParsingTestCase {
|
||||
public CommandLineParserTest() {
|
||||
super("", CommandLineFileType.EXTENSION, true, new CommandLineParserDefinition());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return CommandTestTools.TEST_PATH;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should be ok
|
||||
*/
|
||||
public void testCommandLine() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should have a lot of errors
|
||||
*/
|
||||
public void testJunk() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should have error because option ends with "="
|
||||
*/
|
||||
public void testOptionNoValueJunk() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
}
|
||||
@@ -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.commandInterface.gnuCommandLine;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.commandInterface.gnuCommandLine.psi.CommandLineFile;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Tests references provide correct suggestions for commands, options and argument in command line
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineSuggestionTest extends PyTestCase {
|
||||
/**
|
||||
* Ensures suggestions are correct
|
||||
*/
|
||||
public void testSuggestions() throws Exception {
|
||||
CommandTestTools.initFileType();
|
||||
final CommandLineFile file =
|
||||
(CommandLineFile)myFixture.configureByText(CommandLineFileType.INSTANCE, "command positional_ar --a");
|
||||
file.setCommands(CommandTestTools.createCommands());
|
||||
|
||||
ensureSuggestions("command", "command");
|
||||
ensureSuggestions("positional_ar", "positional_argument", "--option-no-argument", "--available-option");
|
||||
ensureSuggestions("--a", "--option-no-argument", "--available-option");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param initialPositionText place to move cusor to
|
||||
* @param expectedSuggestions expected suggestions
|
||||
*/
|
||||
private void ensureSuggestions(@NotNull final String initialPositionText, @NotNull final String... expectedSuggestions) {
|
||||
moveByText(initialPositionText);
|
||||
final Set<String> completions = new HashSet<String>();
|
||||
for (final LookupElement element : myFixture.completeBasic()) {
|
||||
completions.add(element.getLookupString());
|
||||
}
|
||||
|
||||
Assert.assertThat("Bad suggestions", completions,
|
||||
Matchers.containsInAnyOrder(expectedSuggestions));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return CommandTestTools.TEST_PATH;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user