mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into taggedTemplates
Conflicts: tests/baselines/reference/taggedTemplateStringsTypeArgumentInference.js tests/baselines/reference/taggedTemplateStringsWithOverloadResolution3.j s tests/baselines/reference/taggedTemplateStringsWithTypeErrorInFunctionEx pressionsInSubstitutionExpression.js tests/baselines/reference/templateStringInObjectLiteral.js
This commit is contained in:
+8
-13
@@ -51,17 +51,6 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration has a dynamic name if both of the following are true:
|
||||
* 1. The declaration has a computed property name
|
||||
* 2. The computed name is *not* expressed as Symbol.<name>, where name
|
||||
* is a property of the Symbol constructor that denotes a built in
|
||||
* Symbol.
|
||||
*/
|
||||
export function hasDynamicName(declaration: Declaration): boolean {
|
||||
return declaration.name && declaration.name.kind === SyntaxKind.ComputedPropertyName;
|
||||
}
|
||||
|
||||
export function bindSourceFile(file: SourceFile): void {
|
||||
var start = new Date().getTime();
|
||||
bindSourceFileWorker(file);
|
||||
@@ -98,13 +87,18 @@ module ts {
|
||||
if (symbolKind & SymbolFlags.Value && !symbol.valueDeclaration) symbol.valueDeclaration = node;
|
||||
}
|
||||
|
||||
// Should not be called on a declaration with a computed property name.
|
||||
// Should not be called on a declaration with a computed property name,
|
||||
// unless it is a well known Symbol.
|
||||
function getDeclarationName(node: Declaration): string {
|
||||
if (node.name) {
|
||||
if (node.kind === SyntaxKind.ModuleDeclaration && node.name.kind === SyntaxKind.StringLiteral) {
|
||||
return '"' + (<LiteralExpression>node.name).text + '"';
|
||||
}
|
||||
Debug.assert(!hasDynamicName(node));
|
||||
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
var nameExpression = (<ComputedPropertyName>node.name).expression;
|
||||
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
|
||||
return getPropertyNameForKnownSymbolName((<PropertyAccessExpression>nameExpression).name.text);
|
||||
}
|
||||
return (<Identifier | LiteralExpression>node.name).text;
|
||||
}
|
||||
switch (node.kind) {
|
||||
@@ -496,6 +490,7 @@ module ts {
|
||||
case SyntaxKind.CatchClause:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
bindChildren(node, 0, /*isBlockScopeContainer*/ true);
|
||||
break;
|
||||
|
||||
+340
-120
File diff suppressed because it is too large
Load Diff
@@ -122,12 +122,12 @@ module ts {
|
||||
An_object_member_cannot_be_declared_optional: { code: 1162, category: DiagnosticCategory.Error, key: "An object member cannot be declared optional." },
|
||||
yield_expression_must_be_contained_within_a_generator_declaration: { code: 1163, category: DiagnosticCategory.Error, key: "'yield' expression must be contained_within a generator declaration." },
|
||||
Computed_property_names_are_not_allowed_in_enums: { code: 1164, category: DiagnosticCategory.Error, key: "Computed property names are not allowed in enums." },
|
||||
Computed_property_names_are_not_allowed_in_an_ambient_context: { code: 1165, category: DiagnosticCategory.Error, key: "Computed property names are not allowed in an ambient context." },
|
||||
Computed_property_names_are_not_allowed_in_class_property_declarations: { code: 1166, category: DiagnosticCategory.Error, key: "Computed property names are not allowed in class property declarations." },
|
||||
A_computed_property_name_in_an_ambient_context_must_directly_refer_to_a_built_in_symbol: { code: 1165, category: DiagnosticCategory.Error, key: "A computed property name in an ambient context must directly refer to a built-in symbol." },
|
||||
A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol: { code: 1166, category: DiagnosticCategory.Error, key: "A computed property name in a class property declaration must directly refer to a built-in symbol." },
|
||||
Computed_property_names_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1167, category: DiagnosticCategory.Error, key: "Computed property names are only available when targeting ECMAScript 6 and higher." },
|
||||
Computed_property_names_are_not_allowed_in_method_overloads: { code: 1168, category: DiagnosticCategory.Error, key: "Computed property names are not allowed in method overloads." },
|
||||
Computed_property_names_are_not_allowed_in_interfaces: { code: 1169, category: DiagnosticCategory.Error, key: "Computed property names are not allowed in interfaces." },
|
||||
Computed_property_names_are_not_allowed_in_type_literals: { code: 1170, category: DiagnosticCategory.Error, key: "Computed property names are not allowed in type literals." },
|
||||
A_computed_property_name_in_a_method_overload_must_directly_refer_to_a_built_in_symbol: { code: 1168, category: DiagnosticCategory.Error, key: "A computed property name in a method overload must directly refer to a built-in symbol." },
|
||||
A_computed_property_name_in_an_interface_must_directly_refer_to_a_built_in_symbol: { code: 1169, category: DiagnosticCategory.Error, key: "A computed property name in an interface must directly refer to a built-in symbol." },
|
||||
A_computed_property_name_in_a_type_literal_must_directly_refer_to_a_built_in_symbol: { code: 1170, category: DiagnosticCategory.Error, key: "A computed property name in a type literal must directly refer to a built-in symbol." },
|
||||
A_comma_expression_is_not_allowed_in_a_computed_property_name: { code: 1171, category: DiagnosticCategory.Error, key: "A comma expression is not allowed in a computed property name." },
|
||||
extends_clause_already_seen: { code: 1172, category: DiagnosticCategory.Error, key: "'extends' clause already seen." },
|
||||
extends_clause_must_precede_implements_clause: { code: 1173, category: DiagnosticCategory.Error, key: "'extends' clause must precede 'implements' clause." },
|
||||
@@ -146,6 +146,9 @@ module ts {
|
||||
Merge_conflict_marker_encountered: { code: 1185, category: DiagnosticCategory.Error, key: "Merge conflict marker encountered." },
|
||||
A_rest_element_cannot_have_an_initializer: { code: 1186, category: DiagnosticCategory.Error, key: "A rest element cannot have an initializer." },
|
||||
A_parameter_property_may_not_be_a_binding_pattern: { code: 1187, category: DiagnosticCategory.Error, key: "A parameter property may not be a binding pattern." },
|
||||
Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement: { code: 1188, category: DiagnosticCategory.Error, key: "Only a single variable declaration is allowed in a 'for...of' statement." },
|
||||
The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer: { code: 1189, category: DiagnosticCategory.Error, key: "The variable declaration of a 'for...in' statement cannot have an initializer." },
|
||||
The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer: { code: 1190, category: DiagnosticCategory.Error, key: "The variable declaration of a 'for...of' statement cannot have an initializer." },
|
||||
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
|
||||
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
|
||||
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },
|
||||
@@ -165,7 +168,7 @@ module ts {
|
||||
Global_type_0_must_be_a_class_or_interface_type: { code: 2316, category: DiagnosticCategory.Error, key: "Global type '{0}' must be a class or interface type." },
|
||||
Global_type_0_must_have_1_type_parameter_s: { code: 2317, category: DiagnosticCategory.Error, key: "Global type '{0}' must have {1} type parameter(s)." },
|
||||
Cannot_find_global_type_0: { code: 2318, category: DiagnosticCategory.Error, key: "Cannot find global type '{0}'." },
|
||||
Named_properties_0_of_types_1_and_2_are_not_identical: { code: 2319, category: DiagnosticCategory.Error, key: "Named properties '{0}' of types '{1}' and '{2}' are not identical." },
|
||||
Named_property_0_of_types_1_and_2_are_not_identical: { code: 2319, category: DiagnosticCategory.Error, key: "Named property '{0}' of types '{1}' and '{2}' are not identical." },
|
||||
Interface_0_cannot_simultaneously_extend_types_1_and_2: { code: 2320, category: DiagnosticCategory.Error, key: "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}'." },
|
||||
Excessive_stack_depth_comparing_types_0_and_1: { code: 2321, category: DiagnosticCategory.Error, key: "Excessive stack depth comparing types '{0}' and '{1}'." },
|
||||
Type_0_is_not_assignable_to_type_1: { code: 2322, category: DiagnosticCategory.Error, key: "Type '{0}' is not assignable to type '{1}'." },
|
||||
@@ -187,7 +190,7 @@ module ts {
|
||||
Property_0_does_not_exist_on_type_1: { code: 2339, category: DiagnosticCategory.Error, key: "Property '{0}' does not exist on type '{1}'." },
|
||||
Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword: { code: 2340, category: DiagnosticCategory.Error, key: "Only public and protected methods of the base class are accessible via the 'super' keyword" },
|
||||
Property_0_is_private_and_only_accessible_within_class_1: { code: 2341, category: DiagnosticCategory.Error, key: "Property '{0}' is private and only accessible within class '{1}'." },
|
||||
An_index_expression_argument_must_be_of_type_string_number_or_any: { code: 2342, category: DiagnosticCategory.Error, key: "An index expression argument must be of type 'string', 'number', or 'any'." },
|
||||
An_index_expression_argument_must_be_of_type_string_number_symbol_or_any: { code: 2342, category: DiagnosticCategory.Error, key: "An index expression argument must be of type 'string', 'number', 'symbol, or 'any'." },
|
||||
Type_0_does_not_satisfy_the_constraint_1: { code: 2344, category: DiagnosticCategory.Error, key: "Type '{0}' does not satisfy the constraint '{1}'." },
|
||||
Argument_of_type_0_is_not_assignable_to_parameter_of_type_1: { code: 2345, category: DiagnosticCategory.Error, key: "Argument of type '{0}' is not assignable to parameter of type '{1}'." },
|
||||
Supplied_parameters_do_not_match_any_signature_of_call_target: { code: 2346, category: DiagnosticCategory.Error, key: "Supplied parameters do not match any signature of call target." },
|
||||
@@ -203,7 +206,7 @@ module ts {
|
||||
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer: { code: 2357, category: DiagnosticCategory.Error, key: "The operand of an increment or decrement operator must be a variable, property or indexer." },
|
||||
The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: { code: 2358, category: DiagnosticCategory.Error, key: "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter." },
|
||||
The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type: { code: 2359, category: DiagnosticCategory.Error, key: "The right-hand side of an 'instanceof' expression must be of type 'any' or of a type assignable to the 'Function' interface type." },
|
||||
The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number: { code: 2360, category: DiagnosticCategory.Error, key: "The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'." },
|
||||
The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol: { code: 2360, category: DiagnosticCategory.Error, key: "The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'." },
|
||||
The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: { code: 2361, category: DiagnosticCategory.Error, key: "The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter" },
|
||||
The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2362, category: DiagnosticCategory.Error, key: "The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
|
||||
The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2363, category: DiagnosticCategory.Error, key: "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
|
||||
@@ -298,11 +301,26 @@ module ts {
|
||||
Type_0_is_not_an_array_type: { code: 2461, category: DiagnosticCategory.Error, key: "Type '{0}' is not an array type." },
|
||||
A_rest_element_must_be_last_in_an_array_destructuring_pattern: { code: 2462, category: DiagnosticCategory.Error, key: "A rest element must be last in an array destructuring pattern" },
|
||||
A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature: { code: 2463, category: DiagnosticCategory.Error, key: "A binding pattern parameter cannot be optional in an implementation signature." },
|
||||
A_computed_property_name_must_be_of_type_string_number_or_any: { code: 2464, category: DiagnosticCategory.Error, key: "A computed property name must be of type 'string', 'number', or 'any'." },
|
||||
A_computed_property_name_must_be_of_type_string_number_symbol_or_any: { code: 2464, category: DiagnosticCategory.Error, key: "A computed property name must be of type 'string', 'number', 'symbol', or 'any'." },
|
||||
this_cannot_be_referenced_in_a_computed_property_name: { code: 2465, category: DiagnosticCategory.Error, key: "'this' cannot be referenced in a computed property name." },
|
||||
super_cannot_be_referenced_in_a_computed_property_name: { code: 2466, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in a computed property name." },
|
||||
A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type: { code: 2466, category: DiagnosticCategory.Error, key: "A computed property name cannot reference a type parameter from its containing type." },
|
||||
Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_6_and_higher: { code: 2468, category: DiagnosticCategory.Error, key: "Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher." },
|
||||
A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type: { code: 2467, category: DiagnosticCategory.Error, key: "A computed property name cannot reference a type parameter from its containing type." },
|
||||
Cannot_find_global_value_0: { code: 2468, category: DiagnosticCategory.Error, key: "Cannot find global value '{0}'." },
|
||||
The_0_operator_cannot_be_applied_to_type_symbol: { code: 2469, category: DiagnosticCategory.Error, key: "The '{0}' operator cannot be applied to type 'symbol'." },
|
||||
Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object: { code: 2470, category: DiagnosticCategory.Error, key: "'Symbol' reference does not refer to the global Symbol constructor object." },
|
||||
A_computed_property_name_of_the_form_0_must_be_of_type_symbol: { code: 2471, category: DiagnosticCategory.Error, key: "A computed property name of the form '{0}' must be of type 'symbol'." },
|
||||
Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_6_and_higher: { code: 2472, category: DiagnosticCategory.Error, key: "Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher." },
|
||||
Enum_declarations_must_all_be_const_or_non_const: { code: 2473, category: DiagnosticCategory.Error, key: "Enum declarations must all be const or non-const." },
|
||||
In_const_enum_declarations_member_initializer_must_be_constant_expression: { code: 2474, category: DiagnosticCategory.Error, key: "In 'const' enum declarations member initializer must be constant expression." },
|
||||
const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment: { code: 2475, category: DiagnosticCategory.Error, key: "'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment." },
|
||||
A_const_enum_member_can_only_be_accessed_using_a_string_literal: { code: 2476, category: DiagnosticCategory.Error, key: "A const enum member can only be accessed using a string literal." },
|
||||
const_enum_member_initializer_was_evaluated_to_a_non_finite_value: { code: 2477, category: DiagnosticCategory.Error, key: "'const' enum member initializer was evaluated to a non-finite value." },
|
||||
const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN: { code: 2478, category: DiagnosticCategory.Error, key: "'const' enum member initializer was evaluated to disallowed value 'NaN'." },
|
||||
Property_0_does_not_exist_on_const_enum_1: { code: 2479, category: DiagnosticCategory.Error, key: "Property '{0}' does not exist on 'const' enum '{1}'." },
|
||||
let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations: { code: 2480, category: DiagnosticCategory.Error, key: "'let' is not allowed to be used as a name in 'let' or 'const' declarations." },
|
||||
Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1: { code: 2481, category: DiagnosticCategory.Error, key: "Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'." },
|
||||
for_of_statements_are_only_available_when_targeting_ECMAScript_6_or_higher: { code: 2482, category: DiagnosticCategory.Error, key: "'for...of' statements are only available when targeting ECMAScript 6 or higher." },
|
||||
The_left_hand_side_of_a_for_of_statement_cannot_use_a_type_annotation: { code: 2483, category: DiagnosticCategory.Error, key: "The left-hand side of a 'for...of' statement cannot use a type annotation." },
|
||||
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
|
||||
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
|
||||
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
|
||||
@@ -372,15 +390,6 @@ module ts {
|
||||
Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2: { code: 4077, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using name '{1}' from private module '{2}'." },
|
||||
Parameter_0_of_exported_function_has_or_is_using_private_name_1: { code: 4078, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using private name '{1}'." },
|
||||
Exported_type_alias_0_has_or_is_using_private_name_1: { code: 4081, category: DiagnosticCategory.Error, key: "Exported type alias '{0}' has or is using private name '{1}'." },
|
||||
Enum_declarations_must_all_be_const_or_non_const: { code: 4082, category: DiagnosticCategory.Error, key: "Enum declarations must all be const or non-const." },
|
||||
In_const_enum_declarations_member_initializer_must_be_constant_expression: { code: 4083, category: DiagnosticCategory.Error, key: "In 'const' enum declarations member initializer must be constant expression." },
|
||||
const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment: { code: 4084, category: DiagnosticCategory.Error, key: "'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment." },
|
||||
A_const_enum_member_can_only_be_accessed_using_a_string_literal: { code: 4085, category: DiagnosticCategory.Error, key: "A const enum member can only be accessed using a string literal." },
|
||||
const_enum_member_initializer_was_evaluated_to_a_non_finite_value: { code: 4086, category: DiagnosticCategory.Error, key: "'const' enum member initializer was evaluated to a non-finite value." },
|
||||
const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN: { code: 4087, category: DiagnosticCategory.Error, key: "'const' enum member initializer was evaluated to disallowed value 'NaN'." },
|
||||
Property_0_does_not_exist_on_const_enum_1: { code: 4088, category: DiagnosticCategory.Error, key: "Property '{0}' does not exist on 'const' enum '{1}'." },
|
||||
let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations: { code: 4089, category: DiagnosticCategory.Error, key: "'let' is not allowed to be used as a name in 'let' or 'const' declarations." },
|
||||
Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1: { code: 4090, category: DiagnosticCategory.Error, key: "Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'." },
|
||||
The_current_host_does_not_support_the_0_option: { code: 5001, category: DiagnosticCategory.Error, key: "The current host does not support the '{0}' option." },
|
||||
Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." },
|
||||
Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" },
|
||||
@@ -457,5 +466,6 @@ module ts {
|
||||
yield_expressions_are_not_currently_supported: { code: 9000, category: DiagnosticCategory.Error, key: "'yield' expressions are not currently supported." },
|
||||
Generators_are_not_currently_supported: { code: 9001, category: DiagnosticCategory.Error, key: "Generators are not currently supported." },
|
||||
The_arguments_object_cannot_be_referenced_in_an_arrow_function_Consider_using_a_standard_function_expression: { code: 9002, category: DiagnosticCategory.Error, key: "The 'arguments' object cannot be referenced in an arrow function. Consider using a standard function expression." },
|
||||
for_of_statements_are_not_currently_supported: { code: 9003, category: DiagnosticCategory.Error, key: "'for...of' statements are not currently supported." },
|
||||
};
|
||||
}
|
||||
@@ -479,11 +479,11 @@
|
||||
"category": "Error",
|
||||
"code": 1164
|
||||
},
|
||||
"Computed property names are not allowed in an ambient context.": {
|
||||
"A computed property name in an ambient context must directly refer to a built-in symbol.": {
|
||||
"category": "Error",
|
||||
"code": 1165
|
||||
},
|
||||
"Computed property names are not allowed in class property declarations.": {
|
||||
"A computed property name in a class property declaration must directly refer to a built-in symbol.": {
|
||||
"category": "Error",
|
||||
"code": 1166
|
||||
},
|
||||
@@ -491,15 +491,15 @@
|
||||
"category": "Error",
|
||||
"code": 1167
|
||||
},
|
||||
"Computed property names are not allowed in method overloads.": {
|
||||
"A computed property name in a method overload must directly refer to a built-in symbol.": {
|
||||
"category": "Error",
|
||||
"code": 1168
|
||||
},
|
||||
"Computed property names are not allowed in interfaces.": {
|
||||
"A computed property name in an interface must directly refer to a built-in symbol.": {
|
||||
"category": "Error",
|
||||
"code": 1169
|
||||
},
|
||||
"Computed property names are not allowed in type literals.": {
|
||||
"A computed property name in a type literal must directly refer to a built-in symbol.": {
|
||||
"category": "Error",
|
||||
"code": 1170
|
||||
},
|
||||
@@ -575,6 +575,18 @@
|
||||
"category": "Error",
|
||||
"code": 1187
|
||||
},
|
||||
"Only a single variable declaration is allowed in a 'for...of' statement.": {
|
||||
"category": "Error",
|
||||
"code": 1188
|
||||
},
|
||||
"The variable declaration of a 'for...in' statement cannot have an initializer.": {
|
||||
"category": "Error",
|
||||
"code": 1189
|
||||
},
|
||||
"The variable declaration of a 'for...of' statement cannot have an initializer.": {
|
||||
"category": "Error",
|
||||
"code": 1190
|
||||
},
|
||||
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
@@ -652,7 +664,7 @@
|
||||
"category": "Error",
|
||||
"code": 2318
|
||||
},
|
||||
"Named properties '{0}' of types '{1}' and '{2}' are not identical.": {
|
||||
"Named property '{0}' of types '{1}' and '{2}' are not identical.": {
|
||||
"category": "Error",
|
||||
"code": 2319
|
||||
},
|
||||
@@ -740,7 +752,7 @@
|
||||
"category": "Error",
|
||||
"code": 2341
|
||||
},
|
||||
"An index expression argument must be of type 'string', 'number', or 'any'.": {
|
||||
"An index expression argument must be of type 'string', 'number', 'symbol, or 'any'.": {
|
||||
"category": "Error",
|
||||
"code": 2342
|
||||
},
|
||||
@@ -804,7 +816,7 @@
|
||||
"category": "Error",
|
||||
"code": 2359
|
||||
},
|
||||
"The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.": {
|
||||
"The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
|
||||
"category": "Error",
|
||||
"code": 2360
|
||||
},
|
||||
@@ -1184,7 +1196,7 @@
|
||||
"category": "Error",
|
||||
"code": 2463
|
||||
},
|
||||
"A computed property name must be of type 'string', 'number', or 'any'.": {
|
||||
"A computed property name must be of type 'string', 'number', 'symbol', or 'any'.": {
|
||||
"category": "Error",
|
||||
"code": 2464
|
||||
},
|
||||
@@ -1198,11 +1210,71 @@
|
||||
},
|
||||
"A computed property name cannot reference a type parameter from its containing type.": {
|
||||
"category": "Error",
|
||||
"code": 2466
|
||||
"code": 2467
|
||||
},
|
||||
"Cannot find global value '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2468
|
||||
},
|
||||
"The '{0}' operator cannot be applied to type 'symbol'.": {
|
||||
"category": "Error",
|
||||
"code": 2469
|
||||
},
|
||||
"'Symbol' reference does not refer to the global Symbol constructor object.": {
|
||||
"category": "Error",
|
||||
"code": 2470
|
||||
},
|
||||
"A computed property name of the form '{0}' must be of type 'symbol'.": {
|
||||
"category": "Error",
|
||||
"code": 2471
|
||||
},
|
||||
"Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": {
|
||||
"category": "Error",
|
||||
"code": 2468
|
||||
"code": 2472
|
||||
},
|
||||
"Enum declarations must all be const or non-const.": {
|
||||
"category": "Error",
|
||||
"code": 2473
|
||||
},
|
||||
"In 'const' enum declarations member initializer must be constant expression.": {
|
||||
"category": "Error",
|
||||
"code": 2474
|
||||
},
|
||||
"'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.": {
|
||||
"category": "Error",
|
||||
"code": 2475
|
||||
},
|
||||
"A const enum member can only be accessed using a string literal.": {
|
||||
"category": "Error",
|
||||
"code": 2476
|
||||
},
|
||||
"'const' enum member initializer was evaluated to a non-finite value.": {
|
||||
"category": "Error",
|
||||
"code": 2477
|
||||
},
|
||||
"'const' enum member initializer was evaluated to disallowed value 'NaN'.": {
|
||||
"category": "Error",
|
||||
"code": 2478
|
||||
},
|
||||
"Property '{0}' does not exist on 'const' enum '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2479
|
||||
},
|
||||
"'let' is not allowed to be used as a name in 'let' or 'const' declarations.": {
|
||||
"category": "Error",
|
||||
"code": 2480
|
||||
},
|
||||
"Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2481
|
||||
},
|
||||
"'for...of' statements are only available when targeting ECMAScript 6 or higher.": {
|
||||
"category": "Error",
|
||||
"code": 2482
|
||||
},
|
||||
"The left-hand side of a 'for...of' statement cannot use a type annotation.": {
|
||||
"category": "Error",
|
||||
"code": 2483
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
@@ -1480,43 +1552,7 @@
|
||||
"Exported type alias '{0}' has or is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4081
|
||||
},
|
||||
"Enum declarations must all be const or non-const.": {
|
||||
"category": "Error",
|
||||
"code": 4082
|
||||
},
|
||||
"In 'const' enum declarations member initializer must be constant expression.": {
|
||||
"category": "Error",
|
||||
"code": 4083
|
||||
},
|
||||
"'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.": {
|
||||
"category": "Error",
|
||||
"code": 4084
|
||||
},
|
||||
"A const enum member can only be accessed using a string literal.": {
|
||||
"category": "Error",
|
||||
"code": 4085
|
||||
},
|
||||
"'const' enum member initializer was evaluated to a non-finite value.": {
|
||||
"category": "Error",
|
||||
"code": 4086
|
||||
},
|
||||
"'const' enum member initializer was evaluated to disallowed value 'NaN'.": {
|
||||
"category": "Error",
|
||||
"code": 4087
|
||||
},
|
||||
"Property '{0}' does not exist on 'const' enum '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4088
|
||||
},
|
||||
"'let' is not allowed to be used as a name in 'let' or 'const' declarations.": {
|
||||
"category": "Error",
|
||||
"code": 4089
|
||||
},
|
||||
"Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4090
|
||||
},
|
||||
},
|
||||
"The current host does not support the '{0}' option.": {
|
||||
"category": "Error",
|
||||
"code": 5001
|
||||
@@ -1821,5 +1857,9 @@
|
||||
"The 'arguments' object cannot be referenced in an arrow function. Consider using a standard function expression.": {
|
||||
"category": "Error",
|
||||
"code": 9002
|
||||
},
|
||||
"'for...of' statements are not currently supported.": {
|
||||
"category": "Error",
|
||||
"code": 9003
|
||||
}
|
||||
}
|
||||
|
||||
+563
-163
File diff suppressed because it is too large
Load Diff
+70
-36
@@ -152,6 +152,7 @@ module ts {
|
||||
return visitNode(cbNode, (<PostfixUnaryExpression>node).operand);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
return visitNode(cbNode, (<BinaryExpression>node).left) ||
|
||||
visitNode(cbNode, (<BinaryExpression>node).operatorToken) ||
|
||||
visitNode(cbNode, (<BinaryExpression>node).right);
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return visitNode(cbNode, (<ConditionalExpression>node).condition) ||
|
||||
@@ -191,6 +192,10 @@ module ts {
|
||||
return visitNode(cbNode, (<ForInStatement>node).initializer) ||
|
||||
visitNode(cbNode, (<ForInStatement>node).expression) ||
|
||||
visitNode(cbNode, (<ForInStatement>node).statement);
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return visitNode(cbNode, (<ForOfStatement>node).initializer) ||
|
||||
visitNode(cbNode, (<ForOfStatement>node).expression) ||
|
||||
visitNode(cbNode, (<ForOfStatement>node).statement);
|
||||
case SyntaxKind.ContinueStatement:
|
||||
case SyntaxKind.BreakStatement:
|
||||
return visitNode(cbNode, (<BreakOrContinueStatement>node).label);
|
||||
@@ -1307,6 +1312,12 @@ module ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseTokenNode<T extends Node>(): T {
|
||||
var node = <T>createNode(token);
|
||||
nextToken();
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function canParseSemicolon() {
|
||||
// If there's a real semicolon, then we can always parse it out.
|
||||
if (token === SyntaxKind.SemicolonToken) {
|
||||
@@ -1598,8 +1609,8 @@ module ts {
|
||||
}
|
||||
|
||||
// in the case where we're parsing the variable declarator of a 'for-in' statement, we
|
||||
// are done if we see an 'in' keyword in front of us.
|
||||
if (token === SyntaxKind.InKeyword) {
|
||||
// are done if we see an 'in' keyword in front of us. Same with for-of
|
||||
if (isInOrOfKeyword(token)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1877,6 +1888,7 @@ module ts {
|
||||
case SyntaxKind.BreakStatement:
|
||||
case SyntaxKind.ContinueStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
@@ -2079,14 +2091,6 @@ module ts {
|
||||
return allowIdentifierNames ? parseIdentifierName() : parseIdentifier();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function parseTokenNode<T extends Node>(): T {
|
||||
var node = <T>createNode(token);
|
||||
nextToken();
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseTemplateExpression(): TemplateExpression {
|
||||
var template = <TemplateExpression>createNode(SyntaxKind.TemplateExpression);
|
||||
|
||||
@@ -2593,6 +2597,7 @@ module ts {
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.NumberKeyword:
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
// If these are followed by a dot, then parse these out as a dotted type reference instead.
|
||||
var node = tryParse(parseKeywordAndNoDot);
|
||||
return node || parseTypeReference();
|
||||
@@ -2617,6 +2622,7 @@ module ts {
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.NumberKeyword:
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.TypeOfKeyword:
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
@@ -2794,8 +2800,9 @@ module ts {
|
||||
// Expression[in] , AssignmentExpression[in]
|
||||
|
||||
var expr = parseAssignmentExpressionOrHigher();
|
||||
while (parseOptional(SyntaxKind.CommaToken)) {
|
||||
expr = makeBinaryExpression(expr, SyntaxKind.CommaToken, parseAssignmentExpressionOrHigher());
|
||||
var operatorToken: Node;
|
||||
while ((operatorToken = parseOptionalToken(SyntaxKind.CommaToken))) {
|
||||
expr = makeBinaryExpression(expr, operatorToken, parseAssignmentExpressionOrHigher());
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
@@ -2874,9 +2881,7 @@ module ts {
|
||||
// Note: we call reScanGreaterToken so that we get an appropriately merged token
|
||||
// for cases like > > = becoming >>=
|
||||
if (isLeftHandSideExpression(expr) && isAssignmentOperator(reScanGreaterToken())) {
|
||||
var operator = token;
|
||||
nextToken();
|
||||
return makeBinaryExpression(expr, operator, parseAssignmentExpressionOrHigher());
|
||||
return makeBinaryExpression(expr, parseTokenNode(), parseAssignmentExpressionOrHigher());
|
||||
}
|
||||
|
||||
// It wasn't an assignment or a lambda. This is a conditional expression:
|
||||
@@ -3159,6 +3164,10 @@ module ts {
|
||||
return parseBinaryExpressionRest(precedence, leftOperand);
|
||||
}
|
||||
|
||||
function isInOrOfKeyword(t: SyntaxKind) {
|
||||
return t === SyntaxKind.InKeyword || t === SyntaxKind.OfKeyword;
|
||||
}
|
||||
|
||||
function parseBinaryExpressionRest(precedence: number, leftOperand: Expression): Expression {
|
||||
while (true) {
|
||||
// We either have a binary operator here, or we're finished. We call
|
||||
@@ -3176,9 +3185,7 @@ module ts {
|
||||
break;
|
||||
}
|
||||
|
||||
var operator = token;
|
||||
nextToken();
|
||||
leftOperand = makeBinaryExpression(leftOperand, operator, parseBinaryExpressionOrHigher(newPrecedence));
|
||||
leftOperand = makeBinaryExpression(leftOperand, parseTokenNode(), parseBinaryExpressionOrHigher(newPrecedence));
|
||||
}
|
||||
|
||||
return leftOperand;
|
||||
@@ -3234,10 +3241,10 @@ module ts {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function makeBinaryExpression(left: Expression, operator: SyntaxKind, right: Expression): BinaryExpression {
|
||||
function makeBinaryExpression(left: Expression, operatorToken: Node, right: Expression): BinaryExpression {
|
||||
var node = <BinaryExpression>createNode(SyntaxKind.BinaryExpression, left.pos);
|
||||
node.left = left;
|
||||
node.operator = operator;
|
||||
node.operatorToken = operatorToken;
|
||||
node.right = right;
|
||||
return finishNode(node);
|
||||
}
|
||||
@@ -3788,7 +3795,7 @@ module ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseForOrForInStatement(): Statement {
|
||||
function parseForOrForInOrForOfStatement(): Statement {
|
||||
var pos = getNodePos();
|
||||
parseExpected(SyntaxKind.ForKeyword);
|
||||
parseExpected(SyntaxKind.OpenParenToken);
|
||||
@@ -3796,21 +3803,27 @@ module ts {
|
||||
var initializer: VariableDeclarationList | Expression = undefined;
|
||||
if (token !== SyntaxKind.SemicolonToken) {
|
||||
if (token === SyntaxKind.VarKeyword || token === SyntaxKind.LetKeyword || token === SyntaxKind.ConstKeyword) {
|
||||
initializer = parseVariableDeclarationList(/*disallowIn:*/ true);
|
||||
initializer = parseVariableDeclarationList(/*inForStatementInitializer:*/ true);
|
||||
}
|
||||
else {
|
||||
initializer = disallowInAnd(parseExpression);
|
||||
}
|
||||
}
|
||||
var forOrForInStatement: IterationStatement;
|
||||
var forOrForInOrForOfStatement: IterationStatement;
|
||||
if (parseOptional(SyntaxKind.InKeyword)) {
|
||||
var forInStatement = <ForInStatement>createNode(SyntaxKind.ForInStatement, pos);
|
||||
forInStatement.initializer = initializer;
|
||||
forInStatement.expression = allowInAnd(parseExpression);
|
||||
parseExpected(SyntaxKind.CloseParenToken);
|
||||
forOrForInStatement = forInStatement;
|
||||
forOrForInOrForOfStatement = forInStatement;
|
||||
}
|
||||
else {
|
||||
else if (parseOptional(SyntaxKind.OfKeyword)) {
|
||||
var forOfStatement = <ForOfStatement>createNode(SyntaxKind.ForOfStatement, pos);
|
||||
forOfStatement.initializer = initializer;
|
||||
forOfStatement.expression = allowInAnd(parseAssignmentExpressionOrHigher);
|
||||
parseExpected(SyntaxKind.CloseParenToken);
|
||||
forOrForInOrForOfStatement = forOfStatement;
|
||||
} else {
|
||||
var forStatement = <ForStatement>createNode(SyntaxKind.ForStatement, pos);
|
||||
forStatement.initializer = initializer;
|
||||
parseExpected(SyntaxKind.SemicolonToken);
|
||||
@@ -3822,12 +3835,12 @@ module ts {
|
||||
forStatement.iterator = allowInAnd(parseExpression);
|
||||
}
|
||||
parseExpected(SyntaxKind.CloseParenToken);
|
||||
forOrForInStatement = forStatement;
|
||||
forOrForInOrForOfStatement = forStatement;
|
||||
}
|
||||
|
||||
forOrForInStatement.statement = parseStatement();
|
||||
forOrForInOrForOfStatement.statement = parseStatement();
|
||||
|
||||
return finishNode(forOrForInStatement);
|
||||
return finishNode(forOrForInOrForOfStatement);
|
||||
}
|
||||
|
||||
function parseBreakOrContinueStatement(kind: SyntaxKind): BreakOrContinueStatement {
|
||||
@@ -4073,7 +4086,7 @@ module ts {
|
||||
case SyntaxKind.WhileKeyword:
|
||||
return parseWhileStatement();
|
||||
case SyntaxKind.ForKeyword:
|
||||
return parseForOrForInStatement();
|
||||
return parseForOrForInOrForOfStatement();
|
||||
case SyntaxKind.ContinueKeyword:
|
||||
return parseBreakOrContinueStatement(SyntaxKind.ContinueStatement);
|
||||
case SyntaxKind.BreakKeyword:
|
||||
@@ -4215,11 +4228,13 @@ module ts {
|
||||
var node = <VariableDeclaration>createNode(SyntaxKind.VariableDeclaration);
|
||||
node.name = parseIdentifierOrPattern();
|
||||
node.type = parseTypeAnnotation();
|
||||
node.initializer = parseInitializer(/*inParameter*/ false);
|
||||
if (!isInOrOfKeyword(token)) {
|
||||
node.initializer = parseInitializer(/*inParameter*/ false);
|
||||
}
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseVariableDeclarationList(disallowIn: boolean): VariableDeclarationList {
|
||||
function parseVariableDeclarationList(inForStatementInitializer: boolean): VariableDeclarationList {
|
||||
var node = <VariableDeclarationList>createNode(SyntaxKind.VariableDeclarationList);
|
||||
|
||||
switch (token) {
|
||||
@@ -4236,20 +4251,39 @@ module ts {
|
||||
}
|
||||
|
||||
nextToken();
|
||||
var savedDisallowIn = inDisallowInContext();
|
||||
setDisallowInContext(disallowIn);
|
||||
|
||||
node.declarations = parseDelimitedList(ParsingContext.VariableDeclarations, parseVariableDeclaration);
|
||||
// The user may have written the following:
|
||||
//
|
||||
// for (var of X) { }
|
||||
//
|
||||
// In this case, we want to parse an empty declaration list, and then parse 'of'
|
||||
// as a keyword. The reason this is not automatic is that 'of' is a valid identifier.
|
||||
// So we need to look ahead to determine if 'of' should be treated as a keyword in
|
||||
// this context.
|
||||
// The checker will then give an error that there is an empty declaration list.
|
||||
if (token === SyntaxKind.OfKeyword && lookAhead(canFollowContextualOfKeyword)) {
|
||||
node.declarations = createMissingList<VariableDeclaration>();
|
||||
}
|
||||
else {
|
||||
var savedDisallowIn = inDisallowInContext();
|
||||
setDisallowInContext(inForStatementInitializer);
|
||||
|
||||
setDisallowInContext(savedDisallowIn);
|
||||
node.declarations = parseDelimitedList(ParsingContext.VariableDeclarations, parseVariableDeclaration);
|
||||
|
||||
setDisallowInContext(savedDisallowIn);
|
||||
}
|
||||
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function canFollowContextualOfKeyword(): boolean {
|
||||
return nextTokenIsIdentifier() && nextToken() === SyntaxKind.CloseParenToken;
|
||||
}
|
||||
|
||||
function parseVariableStatement(fullStart: number, modifiers: ModifiersArray): VariableStatement {
|
||||
var node = <VariableStatement>createNode(SyntaxKind.VariableStatement, fullStart);
|
||||
setModifiers(node, modifiers);
|
||||
node.declarationList = parseVariableDeclarationList(/*disallowIn:*/ false);
|
||||
node.declarationList = parseVariableDeclarationList(/*inForStatementInitializer:*/ false);
|
||||
parseSemicolon();
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
+11
-9
@@ -82,6 +82,7 @@ module ts {
|
||||
"string": SyntaxKind.StringKeyword,
|
||||
"super": SyntaxKind.SuperKeyword,
|
||||
"switch": SyntaxKind.SwitchKeyword,
|
||||
"symbol": SyntaxKind.SymbolKeyword,
|
||||
"this": SyntaxKind.ThisKeyword,
|
||||
"throw": SyntaxKind.ThrowKeyword,
|
||||
"true": SyntaxKind.TrueKeyword,
|
||||
@@ -93,6 +94,7 @@ module ts {
|
||||
"while": SyntaxKind.WhileKeyword,
|
||||
"with": SyntaxKind.WithKeyword,
|
||||
"yield": SyntaxKind.YieldKeyword,
|
||||
"of": SyntaxKind.OfKeyword,
|
||||
"{": SyntaxKind.OpenBraceToken,
|
||||
"}": SyntaxKind.CloseBraceToken,
|
||||
"(": SyntaxKind.OpenParenToken,
|
||||
@@ -223,7 +225,7 @@ module ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isUnicodeIdentifierStart(code: number, languageVersion: ScriptTarget) {
|
||||
/* @internal */ export function isUnicodeIdentifierStart(code: number, languageVersion: ScriptTarget) {
|
||||
return languageVersion >= ScriptTarget.ES5 ?
|
||||
lookupInUnicodeMap(code, unicodeES5IdentifierStart) :
|
||||
lookupInUnicodeMap(code, unicodeES3IdentifierStart);
|
||||
@@ -278,13 +280,13 @@ module ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getPositionFromLineAndCharacter(sourceFile: SourceFile, line: number, character: number): number {
|
||||
return computePositionFromLineAndCharacter(getLineStarts(sourceFile), line, character);
|
||||
export function getPositionOfLineAndCharacter(sourceFile: SourceFile, line: number, character: number): number {
|
||||
return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character);
|
||||
}
|
||||
|
||||
export function computePositionFromLineAndCharacter(lineStarts: number[], line: number, character: number): number {
|
||||
Debug.assert(line > 0 && line <= lineStarts.length);
|
||||
return lineStarts[line - 1] + character - 1;
|
||||
export function computePositionOfLineAndCharacter(lineStarts: number[], line: number, character: number): number {
|
||||
Debug.assert(line >= 0 && line < lineStarts.length);
|
||||
return lineStarts[line] + character;
|
||||
}
|
||||
|
||||
export function getLineStarts(sourceFile: SourceFile): number[] {
|
||||
@@ -298,11 +300,11 @@ module ts {
|
||||
// the binary search returns the negative value of the next line start
|
||||
// e.g. if the line starts at [5, 10, 23, 80] and the position requested was 20
|
||||
// then the search will return -2
|
||||
lineNumber = (~lineNumber) - 1;
|
||||
lineNumber = ~lineNumber - 1;
|
||||
}
|
||||
return {
|
||||
line: lineNumber + 1,
|
||||
character: position - lineStarts[lineNumber] + 1
|
||||
line: lineNumber,
|
||||
character: position - lineStarts[lineNumber]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -72,7 +72,7 @@ module ts {
|
||||
function countLines(program: Program): number {
|
||||
var count = 0;
|
||||
forEach(program.getSourceFiles(), file => {
|
||||
count += getLineAndCharacterOfPosition(file, file.end).line;
|
||||
count += getLineStarts(file).length;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
@@ -88,11 +88,11 @@ module ts {
|
||||
if (diagnostic.file) {
|
||||
var loc = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
||||
|
||||
output += diagnostic.file.fileName + "(" + loc.line + "," + loc.character + "): ";
|
||||
output += `${ diagnostic.file.fileName }(${ loc.line + 1 },${ loc.character + 1 }): `;
|
||||
}
|
||||
|
||||
var category = DiagnosticCategory[diagnostic.category].toLowerCase();
|
||||
output += category + " TS" + diagnostic.code + ": " + flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) + sys.newLine;
|
||||
output += `${ category } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
|
||||
|
||||
sys.write(output);
|
||||
}
|
||||
|
||||
+15
-6
@@ -140,8 +140,9 @@ module ts {
|
||||
NumberKeyword,
|
||||
SetKeyword,
|
||||
StringKeyword,
|
||||
SymbolKeyword,
|
||||
TypeKeyword,
|
||||
|
||||
OfKeyword, // LastKeyword and LastToken
|
||||
// Parse tree nodes
|
||||
|
||||
// Names
|
||||
@@ -210,6 +211,7 @@ module ts {
|
||||
WhileStatement,
|
||||
ForStatement,
|
||||
ForInStatement,
|
||||
ForOfStatement,
|
||||
ContinueStatement,
|
||||
BreakStatement,
|
||||
ReturnStatement,
|
||||
@@ -259,7 +261,7 @@ module ts {
|
||||
FirstReservedWord = BreakKeyword,
|
||||
LastReservedWord = WithKeyword,
|
||||
FirstKeyword = BreakKeyword,
|
||||
LastKeyword = TypeKeyword,
|
||||
LastKeyword = OfKeyword,
|
||||
FirstFutureReservedWord = ImplementsKeyword,
|
||||
LastFutureReservedWord = YieldKeyword,
|
||||
FirstTypeNode = TypeReference,
|
||||
@@ -267,7 +269,7 @@ module ts {
|
||||
FirstPunctuation = OpenBraceToken,
|
||||
LastPunctuation = CaretEqualsToken,
|
||||
FirstToken = Unknown,
|
||||
LastToken = TypeKeyword,
|
||||
LastToken = OfKeyword,
|
||||
FirstTriviaToken = SingleLineCommentTrivia,
|
||||
LastTriviaToken = ConflictMarkerTrivia,
|
||||
FirstLiteralToken = NumericLiteral,
|
||||
@@ -621,7 +623,7 @@ module ts {
|
||||
|
||||
export interface BinaryExpression extends Expression {
|
||||
left: Expression;
|
||||
operator: SyntaxKind;
|
||||
operatorToken: Node;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
@@ -752,6 +754,11 @@ module ts {
|
||||
expression: Expression;
|
||||
}
|
||||
|
||||
export interface ForOfStatement extends IterationStatement {
|
||||
initializer: VariableDeclarationList | Expression;
|
||||
expression: Expression;
|
||||
}
|
||||
|
||||
export interface BreakOrContinueStatement extends Statement {
|
||||
label?: Identifier;
|
||||
}
|
||||
@@ -1298,9 +1305,10 @@ module ts {
|
||||
ObjectLiteral = 0x00020000, // Originates in an object literal
|
||||
ContainsUndefinedOrNull = 0x00040000, // Type is or contains Undefined or Null type
|
||||
ContainsObjectLiteral = 0x00080000, // Type is or contains object literal type
|
||||
ESSymbol = 0x00100000, // Type of symbol primitive introduced in ES6
|
||||
|
||||
Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null,
|
||||
Primitive = String | Number | Boolean | Void | Undefined | Null | StringLiteral | Enum,
|
||||
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
|
||||
Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum,
|
||||
StringLike = String | StringLiteral,
|
||||
NumberLike = Number | Enum,
|
||||
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
|
||||
@@ -1638,6 +1646,7 @@ module ts {
|
||||
equals = 0x3D, // =
|
||||
exclamation = 0x21, // !
|
||||
greaterThan = 0x3E, // >
|
||||
hash = 0x23, // #
|
||||
lessThan = 0x3C, // <
|
||||
minus = 0x2D, // -
|
||||
openBrace = 0x7B, // {
|
||||
|
||||
@@ -105,11 +105,16 @@ module ts {
|
||||
return <SourceFile>node;
|
||||
}
|
||||
|
||||
export function getStartPositionOfLine(line: number, sourceFile: SourceFile): number {
|
||||
Debug.assert(line >= 0);
|
||||
return getLineStarts(sourceFile)[line];
|
||||
}
|
||||
|
||||
// This is a useful function for debugging purposes.
|
||||
export function nodePosToString(node: Node): string {
|
||||
var file = getSourceFileOfNode(node);
|
||||
var loc = getLineAndCharacterOfPosition(file, node.pos);
|
||||
return file.fileName + "(" + loc.line + "," + loc.character + ")";
|
||||
return `${ file.fileName }(${ loc.line + 1 },${ loc.character + 1 })`;
|
||||
}
|
||||
|
||||
export function getStartPosOfNode(node: Node): number {
|
||||
@@ -343,6 +348,7 @@ module ts {
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.CaseClause:
|
||||
@@ -560,7 +566,8 @@ module ts {
|
||||
forStatement.condition === node ||
|
||||
forStatement.iterator === node;
|
||||
case SyntaxKind.ForInStatement:
|
||||
var forInStatement = <ForInStatement>parent;
|
||||
case SyntaxKind.ForOfStatement:
|
||||
var forInStatement = <ForInStatement | ForOfStatement>parent;
|
||||
return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
|
||||
forInStatement.expression === node;
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
@@ -688,6 +695,7 @@ module ts {
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
case SyntaxKind.EmptyStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.LabeledStatement:
|
||||
@@ -835,6 +843,54 @@ module ts {
|
||||
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration has a dynamic name if both of the following are true:
|
||||
* 1. The declaration has a computed property name
|
||||
* 2. The computed name is *not* expressed as Symbol.<name>, where name
|
||||
* is a property of the Symbol constructor that denotes a built in
|
||||
* Symbol.
|
||||
*/
|
||||
export function hasDynamicName(declaration: Declaration): boolean {
|
||||
return declaration.name &&
|
||||
declaration.name.kind === SyntaxKind.ComputedPropertyName &&
|
||||
!isWellKnownSymbolSyntactically((<ComputedPropertyName>declaration.name).expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the expression is of the form:
|
||||
* Symbol.name
|
||||
* where Symbol is literally the word "Symbol", and name is any identifierName
|
||||
*/
|
||||
export function isWellKnownSymbolSyntactically(node: Expression): boolean {
|
||||
return node.kind === SyntaxKind.PropertyAccessExpression && isESSymbolIdentifier((<PropertyAccessExpression>node).expression);
|
||||
}
|
||||
|
||||
export function getPropertyNameForPropertyNameNode(name: DeclarationName): string {
|
||||
if (name.kind === SyntaxKind.Identifier || name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral) {
|
||||
return (<Identifier | LiteralExpression>name).text;
|
||||
}
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
var nameExpression = (<ComputedPropertyName>name).expression;
|
||||
if (isWellKnownSymbolSyntactically(nameExpression)) {
|
||||
var rightHandSideName = (<PropertyAccessExpression>nameExpression).name.text;
|
||||
return getPropertyNameForKnownSymbolName(rightHandSideName);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getPropertyNameForKnownSymbolName(symbolName: string): string {
|
||||
return "__@" + symbolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the word "Symbol" with unicode escapes
|
||||
*/
|
||||
export function isESSymbolIdentifier(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "Symbol";
|
||||
}
|
||||
|
||||
export function isModifier(token: SyntaxKind): boolean {
|
||||
switch (token) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
|
||||
+25
-28
@@ -282,6 +282,8 @@ module FourSlash {
|
||||
return new Harness.LanguageService.NativeLanugageServiceAdapter(cancellationToken, compilationOptions);
|
||||
case FourSlashTestType.Shims:
|
||||
return new Harness.LanguageService.ShimLanugageServiceAdapter(cancellationToken, compilationOptions);
|
||||
case FourSlashTestType.Server:
|
||||
return new Harness.LanguageService.ServerLanugageServiceAdapter(cancellationToken, compilationOptions);
|
||||
default:
|
||||
throw new Error("Unknown FourSlash test type: ");
|
||||
}
|
||||
@@ -396,7 +398,7 @@ module FourSlash {
|
||||
|
||||
var lineStarts = ts.computeLineStarts(this.getFileContent(this.activeFile.fileName));
|
||||
var lineCharPos = ts.computeLineAndCharacterOfPosition(lineStarts, pos);
|
||||
this.scenarioActions.push('<MoveCaretToLineAndChar LineNumber="' + lineCharPos.line + '" CharNumber="' + lineCharPos.character + '" />');
|
||||
this.scenarioActions.push(`<MoveCaretToLineAndChar LineNumber=${ lineCharPos.line + 1 } CharNumber=${ lineCharPos.character + 1 } />`);
|
||||
}
|
||||
|
||||
public moveCaretRight(count = 1) {
|
||||
@@ -418,6 +420,9 @@ module FourSlash {
|
||||
this.activeFile = fileToOpen;
|
||||
var fileName = fileToOpen.fileName.replace(Harness.IO.directoryName(fileToOpen.fileName), '').substr(1);
|
||||
this.scenarioActions.push('<OpenFile FileName="" SrcFileId="' + fileName + '" FileId="' + fileName + '" />');
|
||||
|
||||
// Let the host know that this file is now open
|
||||
this.languageServiceAdapterHost.openFile(fileToOpen.fileName);
|
||||
}
|
||||
|
||||
public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) {
|
||||
@@ -1927,7 +1932,7 @@ module FourSlash {
|
||||
}
|
||||
|
||||
var missingItem = { name: name, kind: kind };
|
||||
this.raiseError('verifyGetScriptLexicalStructureListContains failed - could not find the item: ' + JSON.stringify(missingItem) + ' in the returned list: (' + JSON.stringify(items) + ')');
|
||||
this.raiseError('verifyGetScriptLexicalStructureListContains failed - could not find the item: ' + JSON.stringify(missingItem) + ' in the returned list: (' + JSON.stringify(items, null, " ") + ')');
|
||||
}
|
||||
|
||||
private navigationBarItemsContains(items: ts.NavigationBarItem[], name: string, kind: string) {
|
||||
@@ -2010,39 +2015,31 @@ module FourSlash {
|
||||
|
||||
// Get the text of the entire line the caret is currently at
|
||||
private getCurrentLineContent() {
|
||||
// The current caret position (in line/col terms)
|
||||
var line = this.getCurrentCaretFilePosition().line;
|
||||
// The line/col of the start of this line
|
||||
var pos = this.languageServiceAdapterHost.lineColToPosition(this.activeFile.fileName, line, 1);
|
||||
// The index of the current file
|
||||
var text = this.getFileContent(this.activeFile.fileName)
|
||||
|
||||
// The text from the start of the line to the end of the file
|
||||
var text = this.getFileContent(this.activeFile.fileName).substring(pos);
|
||||
var pos = this.currentCaretPosition;
|
||||
var startPos = pos, endPos = pos;
|
||||
|
||||
// Truncate to the first newline
|
||||
var newlinePos = text.indexOf('\n');
|
||||
if (newlinePos === -1) {
|
||||
return text;
|
||||
}
|
||||
else {
|
||||
if (text.charAt(newlinePos - 1) === '\r') {
|
||||
newlinePos--;
|
||||
while (startPos > 0) {
|
||||
var ch = text.charCodeAt(startPos - 1);
|
||||
if (ch === ts.CharacterCodes.carriageReturn || ch === ts.CharacterCodes.lineFeed) {
|
||||
break;
|
||||
}
|
||||
return text.substr(0, newlinePos);
|
||||
}
|
||||
}
|
||||
|
||||
private getCurrentCaretFilePosition() {
|
||||
var result = this.languageServiceAdapterHost.positionToZeroBasedLineCol(this.activeFile.fileName, this.currentCaretPosition);
|
||||
if (result.line >= 0) {
|
||||
result.line++;
|
||||
startPos--;
|
||||
}
|
||||
|
||||
if (result.character >= 0) {
|
||||
result.character++;
|
||||
while (endPos < text.length) {
|
||||
var ch = text.charCodeAt(endPos);
|
||||
|
||||
if (ch === ts.CharacterCodes.carriageReturn || ch === ts.CharacterCodes.lineFeed) {
|
||||
break;
|
||||
}
|
||||
|
||||
endPos++;
|
||||
}
|
||||
|
||||
return result;
|
||||
return text.substring(startPos, endPos);
|
||||
}
|
||||
|
||||
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string) {
|
||||
@@ -2120,7 +2117,7 @@ module FourSlash {
|
||||
}
|
||||
|
||||
private getLineColStringAtPosition(position: number) {
|
||||
var pos = this.languageServiceAdapterHost.positionToZeroBasedLineCol(this.activeFile.fileName, position);
|
||||
var pos = this.languageServiceAdapterHost.positionToLineAndCharacter(this.activeFile.fileName, position);
|
||||
return 'line ' + (pos.line + 1) + ', col ' + pos.character;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
const enum FourSlashTestType {
|
||||
Native,
|
||||
Shims
|
||||
Shims,
|
||||
Server
|
||||
}
|
||||
|
||||
class FourSlashRunner extends RunnerBase {
|
||||
@@ -22,6 +23,10 @@ class FourSlashRunner extends RunnerBase {
|
||||
this.basePath = 'tests/cases/fourslash/shims';
|
||||
this.testSuiteName = 'fourslash-shims';
|
||||
break;
|
||||
case FourSlashTestType.Server:
|
||||
this.basePath = 'tests/cases/fourslash/server';
|
||||
this.testSuiteName = 'fourslash-server';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
/// <reference path='..\services\services.ts' />
|
||||
/// <reference path='..\services\shims.ts' />
|
||||
/// <reference path='..\server\session.ts' />
|
||||
/// <reference path='..\server\client.ts' />
|
||||
/// <reference path='..\server\node.d.ts' />
|
||||
/// <reference path='external\mocha.d.ts'/>
|
||||
/// <reference path='external\chai.d.ts'/>
|
||||
/// <reference path='sourceMapRecorder.ts'/>
|
||||
/// <reference path='runnerbase.ts'/>
|
||||
|
||||
declare var require: any;
|
||||
declare var process: any;
|
||||
var Buffer = require('buffer').Buffer;
|
||||
var Buffer: BufferConstructor = require('buffer').Buffer;
|
||||
|
||||
// this will work in the browser via browserify
|
||||
var _chai: typeof chai = require('chai');
|
||||
@@ -1184,13 +1185,13 @@ module Harness {
|
||||
}
|
||||
|
||||
export function getMinimalDiagnostic(err: ts.Diagnostic): HarnessDiagnostic {
|
||||
var errorLineInfo = err.file ? err.file.getLineAndCharacterFromPosition(err.start) : { line: 0, character: 0 };
|
||||
var errorLineInfo = err.file ? err.file.getLineAndCharacterOfPosition(err.start) : { line: -1, character: -1 };
|
||||
return {
|
||||
fileName: err.file && err.file.fileName,
|
||||
start: err.start,
|
||||
end: err.start + err.length,
|
||||
line: errorLineInfo.line,
|
||||
character: errorLineInfo.character,
|
||||
line: errorLineInfo.line + 1,
|
||||
character: errorLineInfo.character + 1,
|
||||
message: ts.flattenDiagnosticMessageText(err.messageText, ts.sys.newLine),
|
||||
category: ts.DiagnosticCategory[err.category].toLowerCase(),
|
||||
code: err.code
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/// <reference path='..\services\services.ts' />
|
||||
/// <reference path='..\services\shims.ts' />
|
||||
/// <reference path='..\server\client.ts' />
|
||||
/// <reference path='harness.ts' />
|
||||
|
||||
module Harness.LanguageService {
|
||||
@@ -23,18 +24,18 @@ module Harness.LanguageService {
|
||||
this.version++;
|
||||
}
|
||||
|
||||
public editContent(minChar: number, limChar: number, newText: string): void {
|
||||
public editContent(start: number, end: number, newText: string): void {
|
||||
// Apply edits
|
||||
var prefix = this.content.substring(0, minChar);
|
||||
var prefix = this.content.substring(0, start);
|
||||
var middle = newText;
|
||||
var suffix = this.content.substring(limChar);
|
||||
var suffix = this.content.substring(end);
|
||||
this.setContent(prefix + middle + suffix);
|
||||
|
||||
// Store edit range + new length of script
|
||||
this.editRanges.push({
|
||||
length: this.content.length,
|
||||
textChangeRange: ts.createTextChangeRange(
|
||||
ts.createTextSpanFromBounds(minChar, limChar), newText.length)
|
||||
ts.createTextSpanFromBounds(start, end), newText.length)
|
||||
});
|
||||
|
||||
// Update version #
|
||||
@@ -145,52 +146,28 @@ module Harness.LanguageService {
|
||||
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content);
|
||||
}
|
||||
|
||||
public updateScript(fileName: string, content: string) {
|
||||
public editScript(fileName: string, start: number, end: number, newText: string) {
|
||||
var script = this.getScriptInfo(fileName);
|
||||
if (script !== null) {
|
||||
script.updateContent(content);
|
||||
return;
|
||||
}
|
||||
|
||||
this.addScript(fileName, content);
|
||||
}
|
||||
|
||||
public editScript(fileName: string, minChar: number, limChar: number, newText: string) {
|
||||
var script = this.getScriptInfo(fileName);
|
||||
if (script !== null) {
|
||||
script.editContent(minChar, limChar, newText);
|
||||
script.editContent(start, end, newText);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error("No script with name '" + fileName + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1 based index
|
||||
* @param col 1 based index
|
||||
*/
|
||||
public lineColToPosition(fileName: string, line: number, col: number): number {
|
||||
var script: ScriptInfo = this.fileNameToScript[fileName];
|
||||
assert.isNotNull(script);
|
||||
assert.isTrue(line >= 1);
|
||||
assert.isTrue(col >= 1);
|
||||
|
||||
return ts.computePositionFromLineAndCharacter(script.lineMap, line, col);
|
||||
public openFile(fileName: string): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 0 based index
|
||||
* @param col 0 based index
|
||||
*/
|
||||
public positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter {
|
||||
public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter {
|
||||
var script: ScriptInfo = this.fileNameToScript[fileName];
|
||||
assert.isNotNull(script);
|
||||
|
||||
var result = ts.computeLineAndCharacterOfPosition(script.lineMap, position);
|
||||
|
||||
assert.isTrue(result.line >= 1);
|
||||
assert.isTrue(result.character >= 1);
|
||||
return { line: result.line - 1, character: result.character - 1 };
|
||||
return ts.computeLineAndCharacterOfPosition(script.lineMap, position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,10 +213,8 @@ module Harness.LanguageService {
|
||||
getFilenames(): string[] { return this.nativeHost.getFilenames(); }
|
||||
getScriptInfo(fileName: string): ScriptInfo { return this.nativeHost.getScriptInfo(fileName); }
|
||||
addScript(fileName: string, content: string): void { this.nativeHost.addScript(fileName, content); }
|
||||
updateScript(fileName: string, content: string): void { return this.nativeHost.updateScript(fileName, content); }
|
||||
editScript(fileName: string, minChar: number, limChar: number, newText: string): void { this.nativeHost.editScript(fileName, minChar, limChar, newText); }
|
||||
lineColToPosition(fileName: string, line: number, col: number): number { return this.nativeHost.lineColToPosition(fileName, line, col); }
|
||||
positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToZeroBasedLineCol(fileName, position); }
|
||||
editScript(fileName: string, start: number, end: number, newText: string): void { this.nativeHost.editScript(fileName, start, end, newText); }
|
||||
positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToLineAndCharacter(fileName, position); }
|
||||
|
||||
getCompilationSettings(): string { return JSON.stringify(this.nativeHost.getCompilationSettings()); }
|
||||
getCancellationToken(): ts.CancellationToken { return this.nativeHost.getCancellationToken(); }
|
||||
@@ -442,5 +417,156 @@ module Harness.LanguageService {
|
||||
return convertResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Server adapter
|
||||
class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost {
|
||||
private client: ts.server.SessionClient;
|
||||
|
||||
constructor(cancellationToken: ts.CancellationToken, settings: ts.CompilerOptions) {
|
||||
super(cancellationToken, settings);
|
||||
}
|
||||
|
||||
onMessage(message: string): void {
|
||||
|
||||
}
|
||||
|
||||
writeMessage(message: string): void {
|
||||
|
||||
}
|
||||
|
||||
setClient(client: ts.server.SessionClient) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
openFile(fileName: string): void {
|
||||
super.openFile(fileName);
|
||||
this.client.openFile(fileName);
|
||||
}
|
||||
|
||||
editScript(fileName: string, start: number, end: number, newText: string) {
|
||||
super.editScript(fileName, start, end, newText);
|
||||
this.client.changeFile(fileName, start, end, newText);
|
||||
}
|
||||
}
|
||||
|
||||
class SessionServerHost implements ts.server.ServerHost, ts.server.Logger {
|
||||
args: string[] = [];
|
||||
newLine: string;
|
||||
useCaseSensitiveFileNames: boolean = false;
|
||||
|
||||
constructor(private host: NativeLanguageServiceHost) {
|
||||
this.newLine = this.host.getNewLine();
|
||||
}
|
||||
|
||||
onMessage(message: string): void {
|
||||
|
||||
}
|
||||
|
||||
writeMessage(message: string): void {
|
||||
}
|
||||
|
||||
write(message: string): void {
|
||||
this.writeMessage(message);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string {
|
||||
if (fileName.indexOf(Harness.Compiler.defaultLibFileName) >= 0) {
|
||||
fileName = Harness.Compiler.defaultLibFileName;
|
||||
}
|
||||
|
||||
var snapshot = this.host.getScriptSnapshot(fileName);
|
||||
return snapshot && snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
|
||||
writeFile(name: string, text: string, writeByteOrderMark: boolean): void {
|
||||
}
|
||||
|
||||
resolvePath(path: string): string {
|
||||
return path;
|
||||
}
|
||||
|
||||
fileExists(path: string): boolean {
|
||||
return !!this.host.getScriptSnapshot(path);
|
||||
}
|
||||
|
||||
directoryExists(path: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
getExecutingFilePath(): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
exit(exitCode: number): void {
|
||||
}
|
||||
|
||||
createDirectory(directoryName: string): void {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return this.host.getCurrentDirectory();
|
||||
}
|
||||
|
||||
readDirectory(path: string, extension?: string): string[] {
|
||||
throw new Error("Not implemented Yet.");
|
||||
}
|
||||
|
||||
watchFile(fileName: string, callback: (fileName: string) => void): ts.FileWatcher {
|
||||
return { close() { } };
|
||||
}
|
||||
|
||||
close(): void {
|
||||
}
|
||||
|
||||
info(message: string): void {
|
||||
return this.host.log(message);
|
||||
}
|
||||
|
||||
msg(message: string) {
|
||||
return this.host.log(message);
|
||||
}
|
||||
|
||||
endGroup(): void {
|
||||
}
|
||||
|
||||
perftrc(message: string): void {
|
||||
return this.host.log(message);
|
||||
}
|
||||
|
||||
startGroup(): void {
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerLanugageServiceAdapter implements LanguageServiceAdapter {
|
||||
private host: SessionClientHost;
|
||||
private client: ts.server.SessionClient;
|
||||
constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) {
|
||||
// This is the main host that tests use to direct tests
|
||||
var clientHost = new SessionClientHost(cancellationToken, options);
|
||||
var client = new ts.server.SessionClient(clientHost);
|
||||
|
||||
// This host is just a proxy for the clientHost, it uses the client
|
||||
// host to answer server queries about files on disk
|
||||
var serverHost = new SessionServerHost(clientHost);
|
||||
var server = new ts.server.Session(serverHost, serverHost);
|
||||
|
||||
// Fake the connection between the client and the server
|
||||
serverHost.writeMessage = client.onMessage.bind(client);
|
||||
clientHost.writeMessage = server.onMessage.bind(server);
|
||||
|
||||
// Wire the client to the host to get notifications when a file is open
|
||||
// or edited.
|
||||
clientHost.setClient(client);
|
||||
|
||||
// Set the properties
|
||||
this.client = client;
|
||||
this.host = clientHost;
|
||||
}
|
||||
getHost() { return this.host; }
|
||||
getLanguageService(): ts.LanguageService { return this.client; }
|
||||
getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); }
|
||||
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,9 @@ if (testConfigFile !== '') {
|
||||
case 'fourslash-shims':
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
break;
|
||||
case 'fourslash-server':
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
break;
|
||||
case 'fourslash-generated':
|
||||
runners.push(new GeneratedFourslashRunner(FourSlashTestType.Native));
|
||||
break;
|
||||
@@ -95,6 +98,7 @@ if (runners.length === 0) {
|
||||
// language services
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Native));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
//runners.push(new GeneratedFourslashRunner());
|
||||
}
|
||||
|
||||
|
||||
@@ -85,15 +85,17 @@ class TypeWriterWalker {
|
||||
|
||||
private log(node: ts.Node, type: ts.Type): void {
|
||||
var actualPos = ts.skipTrivia(this.currentSourceFile.text, node.pos);
|
||||
var lineAndCharacter = this.currentSourceFile.getLineAndCharacterFromPosition(actualPos);
|
||||
var lineAndCharacter = this.currentSourceFile.getLineAndCharacterOfPosition(actualPos);
|
||||
var sourceText = ts.getTextOfNodeFromSourceText(this.currentSourceFile.text, node);
|
||||
|
||||
// If we got an unknown type, we temporarily want to fall back to just pretending the name
|
||||
// (source text) of the node is the type. This is to align with the old typeWriter to make
|
||||
// baseline comparisons easier. In the long term, we will want to just call typeToString
|
||||
this.results.push({
|
||||
line: lineAndCharacter.line - 1,
|
||||
column: lineAndCharacter.character,
|
||||
line: lineAndCharacter.line,
|
||||
// todo(cyrusn): Not sure why column is one-based for type-writer. But I'm preserving
|
||||
// that behavior to prevent having a lot of baselines to fix up.
|
||||
column: lineAndCharacter.character + 1,
|
||||
syntaxKind: node.kind,
|
||||
sourceText: sourceText,
|
||||
type: this.checker.typeToString(type, node.parent, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.WriteOwnNameForAnyLike)
|
||||
|
||||
Vendored
+39
-39
@@ -1,4 +1,4 @@
|
||||
declare type PropertyKey = string | number | Symbol;
|
||||
declare type PropertyKey = string | number | symbol;
|
||||
|
||||
interface Symbol {
|
||||
/** Returns a string representation of an object. */
|
||||
@@ -7,7 +7,7 @@ interface Symbol {
|
||||
/** Returns the primitive value of the specified object. */
|
||||
valueOf(): Object;
|
||||
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface SymbolConstructor {
|
||||
@@ -20,21 +20,21 @@ interface SymbolConstructor {
|
||||
* Returns a new unique Symbol value.
|
||||
* @param description Description of the new Symbol object.
|
||||
*/
|
||||
(description?: string|number): Symbol;
|
||||
(description?: string|number): symbol;
|
||||
|
||||
/**
|
||||
* Returns a Symbol object from the global symbol registry matching the given key if found.
|
||||
* Otherwise, returns a new symbol with this key.
|
||||
* @param key key to search for.
|
||||
*/
|
||||
for(key: string): Symbol;
|
||||
for(key: string): symbol;
|
||||
|
||||
/**
|
||||
* Returns a key from the global symbol registry matching the given Symbol if found.
|
||||
* Otherwise, returns a undefined.
|
||||
* @param sym Symbol to find the key for.
|
||||
*/
|
||||
keyFor(sym: Symbol): string;
|
||||
keyFor(sym: symbol): string;
|
||||
|
||||
// Well-known Symbols
|
||||
|
||||
@@ -42,42 +42,42 @@ interface SymbolConstructor {
|
||||
* A method that determines if a constructor object recognizes an object as one of the
|
||||
* constructor’s instances. Called by the semantics of the instanceof operator.
|
||||
*/
|
||||
hasInstance: Symbol;
|
||||
hasInstance: symbol;
|
||||
|
||||
/**
|
||||
* A Boolean value that if true indicates that an object should flatten to its array elements
|
||||
* by Array.prototype.concat.
|
||||
*/
|
||||
isConcatSpreadable: Symbol;
|
||||
isConcatSpreadable: symbol;
|
||||
|
||||
/**
|
||||
* A Boolean value that if true indicates that an object may be used as a regular expression.
|
||||
*/
|
||||
isRegExp: Symbol;
|
||||
isRegExp: symbol;
|
||||
|
||||
/**
|
||||
* A method that returns the default iterator for an object.Called by the semantics of the
|
||||
* for-of statement.
|
||||
*/
|
||||
iterator: Symbol;
|
||||
iterator: symbol;
|
||||
|
||||
/**
|
||||
* A method that converts an object to a corresponding primitive value.Called by the ToPrimitive
|
||||
* abstract operation.
|
||||
*/
|
||||
toPrimitive: Symbol;
|
||||
toPrimitive: symbol;
|
||||
|
||||
/**
|
||||
* A String value that is used in the creation of the default string description of an object.
|
||||
* Called by the built- in method Object.prototype.toString.
|
||||
*/
|
||||
toStringTag: Symbol;
|
||||
toStringTag: symbol;
|
||||
|
||||
/**
|
||||
* An Object whose own property names are property names that are excluded from the with
|
||||
* environment bindings of the associated objects.
|
||||
*/
|
||||
unscopables: Symbol;
|
||||
unscopables: symbol;
|
||||
}
|
||||
declare var Symbol: SymbolConstructor;
|
||||
|
||||
@@ -108,7 +108,7 @@ interface ObjectConstructor {
|
||||
* Returns an array of all symbol properties found directly on object o.
|
||||
* @param o Object to retrieve the symbols from.
|
||||
*/
|
||||
getOwnPropertySymbols(o: any): Symbol[];
|
||||
getOwnPropertySymbols(o: any): symbol[];
|
||||
|
||||
/**
|
||||
* Returns true if the values are the same value, false otherwise.
|
||||
@@ -230,7 +230,7 @@ interface ArrayLike<T> {
|
||||
|
||||
interface Array<T> {
|
||||
/** Iterator */
|
||||
// [Symbol.iterator] (): Iterator<T>;
|
||||
[Symbol.iterator] (): Iterator<T>;
|
||||
|
||||
/**
|
||||
* Returns an array of key, value pairs for every entry in the array
|
||||
@@ -329,7 +329,7 @@ interface ArrayConstructor {
|
||||
|
||||
interface String {
|
||||
/** Iterator */
|
||||
// [Symbol.iterator] (): Iterator<string>;
|
||||
[Symbol.iterator] (): Iterator<string>;
|
||||
|
||||
/**
|
||||
* Returns a nonnegative integer Number less than 1114112 (0x110000) that is the code point
|
||||
@@ -447,12 +447,12 @@ interface IteratorResult<T> {
|
||||
}
|
||||
|
||||
interface Iterator<T> {
|
||||
//[Symbol.iterator](): Iterator<T>;
|
||||
[Symbol.iterator](): Iterator<T>;
|
||||
next(): IteratorResult<T>;
|
||||
}
|
||||
|
||||
interface Iterable<T> {
|
||||
//[Symbol.iterator](): Iterator<T>;
|
||||
[Symbol.iterator](): Iterator<T>;
|
||||
}
|
||||
|
||||
interface GeneratorFunction extends Function {
|
||||
@@ -474,7 +474,7 @@ interface Generator<T> extends Iterator<T> {
|
||||
next(value?: any): IteratorResult<T>;
|
||||
throw (exception: any): IteratorResult<T>;
|
||||
return (value: T): IteratorResult<T>;
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface Math {
|
||||
@@ -588,11 +588,11 @@ interface Math {
|
||||
*/
|
||||
cbrt(x: number): number;
|
||||
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface RegExp {
|
||||
// [Symbol.isRegExp]: boolean;
|
||||
[Symbol.isRegExp]: boolean;
|
||||
|
||||
/**
|
||||
* Matches a string with a regular expression, and returns an array containing the results of
|
||||
@@ -649,8 +649,8 @@ interface Map<K, V> {
|
||||
set(key: K, value?: V): Map<K, V>;
|
||||
size: number;
|
||||
values(): Iterator<V>;
|
||||
// [Symbol.iterator]():Iterator<[K,V]>;
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.iterator]():Iterator<[K,V]>;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface MapConstructor {
|
||||
@@ -666,7 +666,7 @@ interface WeakMap<K, V> {
|
||||
get(key: K): V;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): WeakMap<K, V>;
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface WeakMapConstructor {
|
||||
@@ -686,8 +686,8 @@ interface Set<T> {
|
||||
keys(): Iterator<T>;
|
||||
size: number;
|
||||
values(): Iterator<T>;
|
||||
// [Symbol.iterator]():Iterator<T>;
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.iterator]():Iterator<T>;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface SetConstructor {
|
||||
@@ -702,7 +702,7 @@ interface WeakSet<T> {
|
||||
clear(): void;
|
||||
delete(value: T): boolean;
|
||||
has(value: T): boolean;
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface WeakSetConstructor {
|
||||
@@ -713,7 +713,7 @@ interface WeakSetConstructor {
|
||||
declare var WeakSet: WeakSetConstructor;
|
||||
|
||||
interface JSON {
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -733,7 +733,7 @@ interface ArrayBuffer {
|
||||
*/
|
||||
slice(begin: number, end?: number): ArrayBuffer;
|
||||
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface ArrayBufferConstructor {
|
||||
@@ -870,7 +870,7 @@ interface DataView {
|
||||
*/
|
||||
setUint32(byteOffset: number, value: number, littleEndian: boolean): void;
|
||||
|
||||
// [Symbol.toStringTag]: string;
|
||||
[Symbol.toStringTag]: string;
|
||||
}
|
||||
|
||||
interface DataViewConstructor {
|
||||
@@ -1137,7 +1137,7 @@ interface Int8Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Int8ArrayConstructor {
|
||||
@@ -1427,7 +1427,7 @@ interface Uint8Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Uint8ArrayConstructor {
|
||||
@@ -1717,7 +1717,7 @@ interface Uint8ClampedArray {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Uint8ClampedArrayConstructor {
|
||||
@@ -2007,7 +2007,7 @@ interface Int16Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Int16ArrayConstructor {
|
||||
@@ -2297,7 +2297,7 @@ interface Uint16Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Uint16ArrayConstructor {
|
||||
@@ -2587,7 +2587,7 @@ interface Int32Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Int32ArrayConstructor {
|
||||
@@ -2877,7 +2877,7 @@ interface Uint32Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Uint32ArrayConstructor {
|
||||
@@ -3167,7 +3167,7 @@ interface Float32Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Float32ArrayConstructor {
|
||||
@@ -3457,7 +3457,7 @@ interface Float64Array {
|
||||
values(): Iterator<number>;
|
||||
|
||||
[index: number]: number;
|
||||
// [Symbol.iterator] (): Iterator<number>;
|
||||
[Symbol.iterator] (): Iterator<number>;
|
||||
}
|
||||
|
||||
interface Float64ArrayConstructor {
|
||||
@@ -3521,7 +3521,7 @@ declare var Reflect: {
|
||||
getOwnPropertyDescriptor(target: any, propertyKey: PropertyKey): PropertyDescriptor;
|
||||
getPrototypeOf(target: any): any;
|
||||
has(target: any, propertyKey: string): boolean;
|
||||
has(target: any, propertyKey: Symbol): boolean;
|
||||
has(target: any, propertyKey: symbol): boolean;
|
||||
isExtensible(target: any): boolean;
|
||||
ownKeys(target: any): Array<PropertyKey>;
|
||||
preventExtensions(target: any): boolean;
|
||||
|
||||
@@ -0,0 +1,494 @@
|
||||
/// <reference path="session.ts" />
|
||||
|
||||
module ts.server {
|
||||
|
||||
export interface SessionClientHost extends LanguageServiceHost {
|
||||
writeMessage(message: string): void;
|
||||
}
|
||||
|
||||
interface CompletionEntry extends CompletionInfo {
|
||||
fileName: string;
|
||||
position: number;
|
||||
}
|
||||
|
||||
interface RenameEntry extends RenameInfo {
|
||||
fileName: string;
|
||||
position: number;
|
||||
locations: RenameLocation[];
|
||||
findInStrings: boolean;
|
||||
findInComments: boolean;
|
||||
}
|
||||
|
||||
export class SessionClient implements LanguageService {
|
||||
private sequence: number = 0;
|
||||
private fileMapping: ts.Map<string> = {};
|
||||
private lineMaps: ts.Map<number[]> = {};
|
||||
private messages: string[] = [];
|
||||
private lastRenameEntry: RenameEntry;
|
||||
|
||||
constructor(private host: SessionClientHost) {
|
||||
}
|
||||
|
||||
public onMessage(message: string): void {
|
||||
this.messages.push(message);
|
||||
}
|
||||
|
||||
private writeMessage(message: string): void {
|
||||
this.host.writeMessage(message);
|
||||
}
|
||||
|
||||
private getLineMap(fileName: string): number[] {
|
||||
var lineMap = ts.lookUp(this.lineMaps, fileName);
|
||||
if (!lineMap) {
|
||||
var scriptSnapshot = this.host.getScriptSnapshot(fileName);
|
||||
lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
|
||||
}
|
||||
return lineMap;
|
||||
}
|
||||
|
||||
private lineColToPosition(fileName: string, lineCol: protocol.Location): number {
|
||||
return ts.computePositionOfLineAndCharacter(this.getLineMap(fileName), lineCol.line - 1, lineCol.col - 1);
|
||||
}
|
||||
|
||||
private positionToOneBasedLineCol(fileName: string, position: number): protocol.Location {
|
||||
var lineCol = ts.computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
|
||||
return {
|
||||
line: lineCol.line + 1,
|
||||
col: lineCol.character + 1
|
||||
};
|
||||
}
|
||||
|
||||
private convertCodeEditsToTextChange(fileName: string, codeEdit: protocol.CodeEdit): ts.TextChange {
|
||||
var start = this.lineColToPosition(fileName, codeEdit.start);
|
||||
var end = this.lineColToPosition(fileName, codeEdit.end);
|
||||
|
||||
return {
|
||||
span: ts.createTextSpanFromBounds(start, end),
|
||||
newText: codeEdit.newText
|
||||
};
|
||||
}
|
||||
|
||||
private processRequest<T extends protocol.Request>(command: string, arguments?: any): T {
|
||||
var request: protocol.Request = {
|
||||
seq: this.sequence++,
|
||||
type: "request",
|
||||
command: command,
|
||||
arguments: arguments
|
||||
};
|
||||
|
||||
this.writeMessage(JSON.stringify(request));
|
||||
|
||||
return <T>request;
|
||||
}
|
||||
|
||||
private processResponse<T extends protocol.Response>(request: protocol.Request): T {
|
||||
var lastMessage = this.messages.shift();
|
||||
Debug.assert(!!lastMessage, "Did not recieve any responses.");
|
||||
|
||||
// Read the content length
|
||||
var contentLengthPrefix = "Content-Length: ";
|
||||
var lines = lastMessage.split("\r\n");
|
||||
Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
|
||||
|
||||
var contentLengthText = lines[0];
|
||||
Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
|
||||
var contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
|
||||
|
||||
// Read the body
|
||||
var responseBody = lines[2];
|
||||
|
||||
// Verify content length
|
||||
Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
|
||||
|
||||
try {
|
||||
var response: T = JSON.parse(responseBody);
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error("Malformed response: Failed to parse server response: " + lastMessage + ". \r\n Error detailes: " + e.message);
|
||||
}
|
||||
|
||||
// verify the sequence numbers
|
||||
Debug.assert(response.request_seq === request.seq, "Malformed response: response sequance number did not match request sequence number.");
|
||||
|
||||
// unmarshal errors
|
||||
if (!response.success) {
|
||||
throw new Error("Error " + response.message);
|
||||
}
|
||||
|
||||
Debug.assert(!!response.body, "Malformed response: Unexpected empty response body.");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
openFile(fileName: string): void {
|
||||
var args: protocol.FileRequestArgs = { file: fileName };
|
||||
this.processRequest(CommandNames.Open, args);
|
||||
}
|
||||
|
||||
closeFile(fileName: string): void {
|
||||
var args: protocol.FileRequestArgs = { file: fileName };
|
||||
this.processRequest(CommandNames.Close, args);
|
||||
}
|
||||
|
||||
changeFile(fileName: string, start: number, end: number, newText: string): void {
|
||||
// clear the line map after an edit
|
||||
this.lineMaps[fileName] = undefined;
|
||||
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, start);
|
||||
var endLineCol = this.positionToOneBasedLineCol(fileName, end);
|
||||
|
||||
var args: protocol.ChangeRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
endLine: endLineCol.line,
|
||||
endCol: endLineCol.col,
|
||||
insertString: newText
|
||||
};
|
||||
|
||||
this.processRequest(CommandNames.Change, args);
|
||||
}
|
||||
|
||||
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.QuickInfoRequest>(CommandNames.Quickinfo, args);
|
||||
var response = this.processResponse<protocol.QuickInfoResponse>(request);
|
||||
|
||||
var start = this.lineColToPosition(fileName, response.body.start);
|
||||
var end = this.lineColToPosition(fileName, response.body.end);
|
||||
|
||||
return {
|
||||
kind: response.body.kind,
|
||||
kindModifiers: response.body.kindModifiers,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
displayParts: [{ kind: "text", text: response.body.displayString }],
|
||||
documentation: [{ kind: "text", text: response.body.documentation }]
|
||||
};
|
||||
}
|
||||
|
||||
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.CompletionsRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
prefix: undefined
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.CompletionsRequest>(CommandNames.Completions, args);
|
||||
var response = this.processResponse<protocol.CompletionsResponse>(request);
|
||||
|
||||
return {
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: false,
|
||||
entries: response.body,
|
||||
fileName: fileName,
|
||||
position: position
|
||||
};
|
||||
}
|
||||
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.CompletionDetailsRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
entryNames: [entryName]
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
|
||||
var response = this.processResponse<protocol.CompletionDetailsResponse>(request);
|
||||
Debug.assert(response.body.length == 1, "Unexpected length of completion details response body.");
|
||||
return response.body[0];
|
||||
}
|
||||
|
||||
getNavigateToItems(searchTerm: string): NavigateToItem[] {
|
||||
var args: protocol.NavtoRequestArgs = {
|
||||
searchTerm,
|
||||
file: this.host.getScriptFileNames()[0]
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.NavtoRequest>(CommandNames.Navto, args);
|
||||
var response = this.processResponse<protocol.NavtoResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineColToPosition(fileName, entry.start);
|
||||
var end = this.lineColToPosition(fileName, entry.end);
|
||||
|
||||
return {
|
||||
name: entry.name,
|
||||
containerName: entry.containerName || "",
|
||||
containerKind: entry.containerKind || "",
|
||||
kind: entry.kind,
|
||||
kindModifiers: entry.kindModifiers,
|
||||
matchKind: entry.matchKind,
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): ts.TextChange[] {
|
||||
var startLineCol = this.positionToOneBasedLineCol(fileName, start);
|
||||
var endLineCol = this.positionToOneBasedLineCol(fileName, end);
|
||||
var args: protocol.FormatRequestArgs = {
|
||||
file: fileName,
|
||||
line: startLineCol.line,
|
||||
col: startLineCol.col,
|
||||
endLine: endLineCol.line,
|
||||
endCol: endLineCol.col,
|
||||
};
|
||||
|
||||
// TODO: handle FormatCodeOptions
|
||||
var request = this.processRequest<protocol.FormatRequest>(CommandNames.Format, args);
|
||||
var response = this.processResponse<protocol.FormatResponse>(request);
|
||||
|
||||
return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
|
||||
}
|
||||
|
||||
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
|
||||
return this.getFormattingEditsForRange(fileName, 0, this.host.getScriptSnapshot(fileName).getLength(), options);
|
||||
}
|
||||
|
||||
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): ts.TextChange[] {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.FormatOnKeyRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
key: key
|
||||
};
|
||||
|
||||
// TODO: handle FormatCodeOptions
|
||||
var request = this.processRequest<protocol.FormatOnKeyRequest>(CommandNames.Formatonkey, args);
|
||||
var response = this.processResponse<protocol.FormatResponse>(request);
|
||||
|
||||
return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
|
||||
}
|
||||
|
||||
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.DefinitionRequest>(CommandNames.Definition, args);
|
||||
var response = this.processResponse<protocol.DefinitionResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineColToPosition(fileName, entry.start);
|
||||
var end = this.lineColToPosition(fileName, entry.end);
|
||||
return {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: "",
|
||||
name: ""
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.ReferencesRequest>(CommandNames.References, args);
|
||||
var response = this.processResponse<protocol.ReferencesResponse>(request);
|
||||
|
||||
return response.body.refs.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineColToPosition(fileName, entry.start);
|
||||
var end = this.lineColToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
isWriteAccess: entry.isWriteAccess,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getEmitOutput(fileName: string): EmitOutput {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getSyntacticDiagnostics(fileName: string): Diagnostic[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getSemanticDiagnostics(fileName: string): Diagnostic[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getCompilerOptionsDiagnostics(): Diagnostic[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.RenameRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
findInStrings,
|
||||
findInComments
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.RenameRequest>(CommandNames.Rename, args);
|
||||
var response = this.processResponse<protocol.RenameResponse>(request);
|
||||
var locations: RenameLocation[] = [];
|
||||
response.body.locs.map((entry: protocol.SpanGroup) => {
|
||||
var fileName = entry.file;
|
||||
entry.locs.map((loc: protocol.TextSpan) => {
|
||||
var start = this.lineColToPosition(fileName, loc.start);
|
||||
var end = this.lineColToPosition(fileName, loc.end);
|
||||
locations.push({
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
fileName: fileName
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.lastRenameEntry = {
|
||||
canRename: response.body.info.canRename,
|
||||
displayName: response.body.info.displayName,
|
||||
fullDisplayName: response.body.info.fullDisplayName,
|
||||
kind: response.body.info.kind,
|
||||
kindModifiers: response.body.info.kindModifiers,
|
||||
localizedErrorMessage: response.body.info.localizedErrorMessage,
|
||||
triggerSpan: ts.createTextSpanFromBounds(position, position),
|
||||
fileName: fileName,
|
||||
position: position,
|
||||
findInStrings: findInStrings,
|
||||
findInComments: findInComments,
|
||||
locations: locations
|
||||
};
|
||||
}
|
||||
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] {
|
||||
if (!this.lastRenameEntry ||
|
||||
this.lastRenameEntry.fileName !== fileName ||
|
||||
this.lastRenameEntry.position !== position ||
|
||||
this.lastRenameEntry.findInStrings != findInStrings ||
|
||||
this.lastRenameEntry.findInComments != findInComments) {
|
||||
this.getRenameInfo(fileName, position, findInStrings, findInComments);
|
||||
}
|
||||
|
||||
return this.lastRenameEntry.locations;
|
||||
}
|
||||
|
||||
decodeNavigationBarItems(items: protocol.NavigationBarItem[], fileName: string): NavigationBarItem[] {
|
||||
if (!items) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return items.map(item => ({
|
||||
text: item.text,
|
||||
kind: item.kind,
|
||||
kindModifiers: item.kindModifiers || "",
|
||||
spans: item.spans.map(span=> createTextSpanFromBounds(this.lineColToPosition(fileName, span.start), this.lineColToPosition(fileName, span.end))),
|
||||
childItems: this.decodeNavigationBarItems(item.childItems, fileName),
|
||||
indent: 0,
|
||||
bolded: false,
|
||||
grayed: false
|
||||
}));
|
||||
}
|
||||
|
||||
getNavigationBarItems(fileName: string): NavigationBarItem[] {
|
||||
var args: protocol.FileRequestArgs = {
|
||||
file: fileName
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.NavBarRequest>(CommandNames.NavBar, args);
|
||||
var response = this.processResponse<protocol.NavBarResponse>(request);
|
||||
|
||||
return this.decodeNavigationBarItems(response.body, fileName);
|
||||
}
|
||||
|
||||
getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getOutliningSpans(fileName: string): OutliningSpan[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
|
||||
var lineCol = this.positionToOneBasedLineCol(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineCol.line,
|
||||
col: lineCol.col,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.BraceRequest>(CommandNames.Brace, args);
|
||||
var response = this.processResponse<protocol.BraceResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var start = this.lineColToPosition(fileName, entry.start);
|
||||
var end = this.lineColToPosition(fileName, entry.end);
|
||||
return {
|
||||
start: start,
|
||||
length: end - start,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getProgram(): Program {
|
||||
throw new Error("SourceFile objects are not serializable through the server protocol.");
|
||||
}
|
||||
|
||||
getSourceFile(fileName: string): SourceFile {
|
||||
throw new Error("SourceFile objects are not serializable through the server protocol.");
|
||||
}
|
||||
|
||||
cleanupSemanticCache(): void {
|
||||
throw new Error("cleanupSemanticCache is not available through the server layer.");
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
throw new Error("dispose is not available through the server layer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Vendored
+677
@@ -0,0 +1,677 @@
|
||||
// Type definitions for Node.js v0.10.1
|
||||
// Project: http://nodejs.org/
|
||||
// Definitions by: Microsoft TypeScript <http://typescriptlang.org>, DefinitelyTyped <https://github.com/borisyankov/DefinitelyTyped>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
/************************************************
|
||||
* *
|
||||
* Node.js v0.10.1 API *
|
||||
* *
|
||||
************************************************/
|
||||
|
||||
/************************************************
|
||||
* *
|
||||
* GLOBAL *
|
||||
* *
|
||||
************************************************/
|
||||
declare var process: NodeJS.Process;
|
||||
declare var global: any;
|
||||
|
||||
declare var __filename: string;
|
||||
declare var __dirname: string;
|
||||
|
||||
declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;
|
||||
declare function clearTimeout(timeoutId: NodeJS.Timer): void;
|
||||
declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;
|
||||
declare function clearInterval(intervalId: NodeJS.Timer): void;
|
||||
declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;
|
||||
declare function clearImmediate(immediateId: any): void;
|
||||
|
||||
declare var require: {
|
||||
(id: string): any;
|
||||
resolve(id: string): string;
|
||||
cache: any;
|
||||
extensions: any;
|
||||
main: any;
|
||||
};
|
||||
|
||||
declare var module: {
|
||||
exports: any;
|
||||
require(id: string): any;
|
||||
id: string;
|
||||
filename: string;
|
||||
loaded: boolean;
|
||||
parent: any;
|
||||
children: any[];
|
||||
};
|
||||
|
||||
// Same as module.exports
|
||||
declare var exports: any;
|
||||
declare var SlowBuffer: {
|
||||
new (str: string, encoding?: string): Buffer;
|
||||
new (size: number): Buffer;
|
||||
new (size: Uint8Array): Buffer;
|
||||
new (array: any[]): Buffer;
|
||||
prototype: Buffer;
|
||||
isBuffer(obj: any): boolean;
|
||||
byteLength(string: string, encoding?: string): number;
|
||||
concat(list: Buffer[], totalLength?: number): Buffer;
|
||||
};
|
||||
|
||||
|
||||
// Buffer class
|
||||
interface Buffer extends NodeBuffer { }
|
||||
interface BufferConstructor {
|
||||
new (str: string, encoding ?: string): Buffer;
|
||||
new (size: number): Buffer;
|
||||
new (size: Uint8Array): Buffer;
|
||||
new (array: any[]): Buffer;
|
||||
prototype: Buffer;
|
||||
isBuffer(obj: any): boolean;
|
||||
byteLength(string: string, encoding ?: string): number;
|
||||
concat(list: Buffer[], totalLength ?: number): Buffer;
|
||||
}
|
||||
declare var Buffer: BufferConstructor;
|
||||
|
||||
/************************************************
|
||||
* *
|
||||
* GLOBAL INTERFACES *
|
||||
* *
|
||||
************************************************/
|
||||
declare module NodeJS {
|
||||
export interface ErrnoException extends Error {
|
||||
errno?: any;
|
||||
code?: string;
|
||||
path?: string;
|
||||
syscall?: string;
|
||||
}
|
||||
|
||||
export interface EventEmitter {
|
||||
addListener(event: string, listener: Function): EventEmitter;
|
||||
on(event: string, listener: Function): EventEmitter;
|
||||
once(event: string, listener: Function): EventEmitter;
|
||||
removeListener(event: string, listener: Function): EventEmitter;
|
||||
removeAllListeners(event?: string): EventEmitter;
|
||||
setMaxListeners(n: number): void;
|
||||
listeners(event: string): Function[];
|
||||
emit(event: string, ...args: any[]): boolean;
|
||||
}
|
||||
|
||||
export interface ReadableStream extends EventEmitter {
|
||||
readable: boolean;
|
||||
read(size?: number): any;
|
||||
setEncoding(encoding: string): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
pipe<T extends WritableStream>(destination: T, options?: { end?: boolean; }): T;
|
||||
unpipe<T extends WritableStream>(destination?: T): void;
|
||||
unshift(chunk: string): void;
|
||||
unshift(chunk: Buffer): void;
|
||||
wrap(oldStream: ReadableStream): ReadableStream;
|
||||
}
|
||||
|
||||
export interface WritableStream extends EventEmitter {
|
||||
writable: boolean;
|
||||
write(buffer: Buffer, cb?: Function): boolean;
|
||||
write(str: string, cb?: Function): boolean;
|
||||
write(str: string, encoding?: string, cb?: Function): boolean;
|
||||
end(): void;
|
||||
end(buffer: Buffer, cb?: Function): void;
|
||||
end(str: string, cb?: Function): void;
|
||||
end(str: string, encoding?: string, cb?: Function): void;
|
||||
}
|
||||
|
||||
export interface ReadWriteStream extends ReadableStream, WritableStream { }
|
||||
|
||||
export interface Process extends EventEmitter {
|
||||
stdout: WritableStream;
|
||||
stderr: WritableStream;
|
||||
stdin: ReadableStream;
|
||||
argv: string[];
|
||||
execPath: string;
|
||||
abort(): void;
|
||||
chdir(directory: string): void;
|
||||
cwd(): string;
|
||||
env: any;
|
||||
exit(code?: number): void;
|
||||
getgid(): number;
|
||||
setgid(id: number): void;
|
||||
setgid(id: string): void;
|
||||
getuid(): number;
|
||||
setuid(id: number): void;
|
||||
setuid(id: string): void;
|
||||
version: string;
|
||||
versions: {
|
||||
http_parser: string;
|
||||
node: string;
|
||||
v8: string;
|
||||
ares: string;
|
||||
uv: string;
|
||||
zlib: string;
|
||||
openssl: string;
|
||||
};
|
||||
config: {
|
||||
target_defaults: {
|
||||
cflags: any[];
|
||||
default_configuration: string;
|
||||
defines: string[];
|
||||
include_dirs: string[];
|
||||
libraries: string[];
|
||||
};
|
||||
variables: {
|
||||
clang: number;
|
||||
host_arch: string;
|
||||
node_install_npm: boolean;
|
||||
node_install_waf: boolean;
|
||||
node_prefix: string;
|
||||
node_shared_openssl: boolean;
|
||||
node_shared_v8: boolean;
|
||||
node_shared_zlib: boolean;
|
||||
node_use_dtrace: boolean;
|
||||
node_use_etw: boolean;
|
||||
node_use_openssl: boolean;
|
||||
target_arch: string;
|
||||
v8_no_strict_aliasing: number;
|
||||
v8_use_snapshot: boolean;
|
||||
visibility: string;
|
||||
};
|
||||
};
|
||||
kill(pid: number, signal?: string): void;
|
||||
pid: number;
|
||||
title: string;
|
||||
arch: string;
|
||||
platform: string;
|
||||
memoryUsage(): { rss: number; heapTotal: number; heapUsed: number; };
|
||||
nextTick(callback: Function): void;
|
||||
umask(mask?: number): number;
|
||||
uptime(): number;
|
||||
hrtime(time?: number[]): number[];
|
||||
|
||||
// Worker
|
||||
send? (message: any, sendHandle?: any): void;
|
||||
}
|
||||
|
||||
export interface Timer {
|
||||
ref(): void;
|
||||
unref(): void;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
interface NodeBuffer {
|
||||
[index: number]: number;
|
||||
write(string: string, offset?: number, length?: number, encoding?: string): number;
|
||||
toString(encoding?: string, start?: number, end?: number): string;
|
||||
toJSON(): any;
|
||||
length: number;
|
||||
copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;
|
||||
slice(start?: number, end?: number): Buffer;
|
||||
readUInt8(offset: number, noAsset?: boolean): number;
|
||||
readUInt16LE(offset: number, noAssert?: boolean): number;
|
||||
readUInt16BE(offset: number, noAssert?: boolean): number;
|
||||
readUInt32LE(offset: number, noAssert?: boolean): number;
|
||||
readUInt32BE(offset: number, noAssert?: boolean): number;
|
||||
readInt8(offset: number, noAssert?: boolean): number;
|
||||
readInt16LE(offset: number, noAssert?: boolean): number;
|
||||
readInt16BE(offset: number, noAssert?: boolean): number;
|
||||
readInt32LE(offset: number, noAssert?: boolean): number;
|
||||
readInt32BE(offset: number, noAssert?: boolean): number;
|
||||
readFloatLE(offset: number, noAssert?: boolean): number;
|
||||
readFloatBE(offset: number, noAssert?: boolean): number;
|
||||
readDoubleLE(offset: number, noAssert?: boolean): number;
|
||||
readDoubleBE(offset: number, noAssert?: boolean): number;
|
||||
writeUInt8(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeUInt16LE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeUInt16BE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeUInt32LE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeUInt32BE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeInt8(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeInt16LE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeInt16BE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeInt32LE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeInt32BE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeFloatLE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeFloatBE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeDoubleLE(value: number, offset: number, noAssert?: boolean): void;
|
||||
writeDoubleBE(value: number, offset: number, noAssert?: boolean): void;
|
||||
fill(value: any, offset?: number, end?: number): void;
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
export interface Path {
|
||||
normalize(p: string): string;
|
||||
join(...paths: any[]): string;
|
||||
resolve(...pathSegments: any[]): string;
|
||||
relative(from: string, to: string): string;
|
||||
dirname(p: string): string;
|
||||
basename(p: string, ext?: string): string;
|
||||
extname(p: string): string;
|
||||
sep: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
export interface ReadLineInstance extends EventEmitter {
|
||||
setPrompt(prompt: string, length: number): void;
|
||||
prompt(preserveCursor?: boolean): void;
|
||||
question(query: string, callback: Function): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
close(): void;
|
||||
write(data: any, key?: any): void;
|
||||
}
|
||||
export interface ReadLineOptions {
|
||||
input: NodeJS.ReadableStream;
|
||||
output: NodeJS.WritableStream;
|
||||
completer?: Function;
|
||||
terminal?: boolean;
|
||||
}
|
||||
|
||||
export interface ReadLine {
|
||||
createInterface(options: ReadLineOptions): ReadLineInstance;
|
||||
}
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
module events {
|
||||
export class EventEmitter implements NodeJS.EventEmitter {
|
||||
static listenerCount(emitter: EventEmitter, event: string): number;
|
||||
|
||||
addListener(event: string, listener: Function): EventEmitter;
|
||||
on(event: string, listener: Function): EventEmitter;
|
||||
once(event: string, listener: Function): EventEmitter;
|
||||
removeListener(event: string, listener: Function): EventEmitter;
|
||||
removeAllListeners(event?: string): EventEmitter;
|
||||
setMaxListeners(n: number): void;
|
||||
listeners(event: string): Function[];
|
||||
emit(event: string, ...args: any[]): boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
module stream {
|
||||
|
||||
export interface Stream extends events.EventEmitter {
|
||||
pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T;
|
||||
}
|
||||
|
||||
export interface ReadableOptions {
|
||||
highWaterMark?: number;
|
||||
encoding?: string;
|
||||
objectMode?: boolean;
|
||||
}
|
||||
|
||||
export class Readable extends events.EventEmitter implements NodeJS.ReadableStream {
|
||||
readable: boolean;
|
||||
constructor(opts?: ReadableOptions);
|
||||
_read(size: number): void;
|
||||
read(size?: number): any;
|
||||
setEncoding(encoding: string): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T;
|
||||
unpipe<T extends NodeJS.WritableStream>(destination?: T): void;
|
||||
unshift(chunk: string): void;
|
||||
unshift(chunk: Buffer): void;
|
||||
wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream;
|
||||
push(chunk: any, encoding?: string): boolean;
|
||||
}
|
||||
|
||||
export interface WritableOptions {
|
||||
highWaterMark?: number;
|
||||
decodeStrings?: boolean;
|
||||
}
|
||||
|
||||
export class Writable extends events.EventEmitter implements NodeJS.WritableStream {
|
||||
writable: boolean;
|
||||
constructor(opts?: WritableOptions);
|
||||
_write(data: Buffer, encoding: string, callback: Function): void;
|
||||
_write(data: string, encoding: string, callback: Function): void;
|
||||
write(buffer: Buffer, cb?: Function): boolean;
|
||||
write(str: string, cb?: Function): boolean;
|
||||
write(str: string, encoding?: string, cb?: Function): boolean;
|
||||
end(): void;
|
||||
end(buffer: Buffer, cb?: Function): void;
|
||||
end(str: string, cb?: Function): void;
|
||||
end(str: string, encoding?: string, cb?: Function): void;
|
||||
}
|
||||
|
||||
export interface DuplexOptions extends ReadableOptions, WritableOptions {
|
||||
allowHalfOpen?: boolean;
|
||||
}
|
||||
|
||||
// Note: Duplex extends both Readable and Writable.
|
||||
export class Duplex extends Readable implements NodeJS.ReadWriteStream {
|
||||
writable: boolean;
|
||||
constructor(opts?: DuplexOptions);
|
||||
_write(data: Buffer, encoding: string, callback: Function): void;
|
||||
_write(data: string, encoding: string, callback: Function): void;
|
||||
write(buffer: Buffer, cb?: Function): boolean;
|
||||
write(str: string, cb?: Function): boolean;
|
||||
write(str: string, encoding?: string, cb?: Function): boolean;
|
||||
end(): void;
|
||||
end(buffer: Buffer, cb?: Function): void;
|
||||
end(str: string, cb?: Function): void;
|
||||
end(str: string, encoding?: string, cb?: Function): void;
|
||||
}
|
||||
|
||||
export interface TransformOptions extends ReadableOptions, WritableOptions { }
|
||||
|
||||
// Note: Transform lacks the _read and _write methods of Readable/Writable.
|
||||
export class Transform extends events.EventEmitter implements NodeJS.ReadWriteStream {
|
||||
readable: boolean;
|
||||
writable: boolean;
|
||||
constructor(opts?: TransformOptions);
|
||||
_transform(chunk: Buffer, encoding: string, callback: Function): void;
|
||||
_transform(chunk: string, encoding: string, callback: Function): void;
|
||||
_flush(callback: Function): void;
|
||||
read(size?: number): any;
|
||||
setEncoding(encoding: string): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T;
|
||||
unpipe<T extends NodeJS.WritableStream>(destination?: T): void;
|
||||
unshift(chunk: string): void;
|
||||
unshift(chunk: Buffer): void;
|
||||
wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream;
|
||||
push(chunk: any, encoding?: string): boolean;
|
||||
write(buffer: Buffer, cb?: Function): boolean;
|
||||
write(str: string, cb?: Function): boolean;
|
||||
write(str: string, encoding?: string, cb?: Function): boolean;
|
||||
end(): void;
|
||||
end(buffer: Buffer, cb?: Function): void;
|
||||
end(str: string, cb?: Function): void;
|
||||
end(str: string, encoding?: string, cb?: Function): void;
|
||||
}
|
||||
|
||||
export class PassThrough extends Transform { }
|
||||
}
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
module fs {
|
||||
interface Stats {
|
||||
isFile(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isBlockDevice(): boolean;
|
||||
isCharacterDevice(): boolean;
|
||||
isSymbolicLink(): boolean;
|
||||
isFIFO(): boolean;
|
||||
isSocket(): boolean;
|
||||
dev: number;
|
||||
ino: number;
|
||||
mode: number;
|
||||
nlink: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
rdev: number;
|
||||
size: number;
|
||||
blksize: number;
|
||||
blocks: number;
|
||||
atime: Date;
|
||||
mtime: Date;
|
||||
ctime: Date;
|
||||
}
|
||||
interface FSWatcher extends events.EventEmitter {
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export interface ReadStream extends stream.Readable { }
|
||||
export interface WriteStream extends stream.Writable { }
|
||||
|
||||
export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function renameSync(oldPath: string, newPath: string): void;
|
||||
export function truncate(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function truncate(path: string, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function truncateSync(path: string, len?: number): void;
|
||||
export function ftruncate(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function ftruncate(fd: number, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function ftruncateSync(fd: number, len?: number): void;
|
||||
export function chown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function chownSync(path: string, uid: number, gid: number): void;
|
||||
export function fchown(fd: number, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function fchownSync(fd: number, uid: number, gid: number): void;
|
||||
export function lchown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function lchownSync(path: string, uid: number, gid: number): void;
|
||||
export function chmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function chmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function chmodSync(path: string, mode: number): void;
|
||||
export function chmodSync(path: string, mode: string): void;
|
||||
export function fchmod(fd: number, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function fchmod(fd: number, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function fchmodSync(fd: number, mode: number): void;
|
||||
export function fchmodSync(fd: number, mode: string): void;
|
||||
export function lchmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function lchmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function lchmodSync(path: string, mode: number): void;
|
||||
export function lchmodSync(path: string, mode: string): void;
|
||||
export function stat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void;
|
||||
export function lstat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void;
|
||||
export function fstat(fd: number, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void;
|
||||
export function statSync(path: string): Stats;
|
||||
export function lstatSync(path: string): Stats;
|
||||
export function fstatSync(fd: number): Stats;
|
||||
export function link(srcpath: string, dstpath: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function linkSync(srcpath: string, dstpath: string): void;
|
||||
export function symlink(srcpath: string, dstpath: string, type?: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function symlinkSync(srcpath: string, dstpath: string, type?: string): void;
|
||||
export function readlink(path: string, callback?: (err: NodeJS.ErrnoException, linkString: string) => any): void;
|
||||
export function readlinkSync(path: string): string;
|
||||
export function realpath(path: string, callback?: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void;
|
||||
export function realpath(path: string, cache: { [path: string]: string }, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void;
|
||||
export function realpathSync(path: string, cache?: { [path: string]: string }): string;
|
||||
export function unlink(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function unlinkSync(path: string): void;
|
||||
export function rmdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function rmdirSync(path: string): void;
|
||||
export function mkdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function mkdir(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function mkdir(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function mkdirSync(path: string, mode?: number): void;
|
||||
export function mkdirSync(path: string, mode?: string): void;
|
||||
export function readdir(path: string, callback?: (err: NodeJS.ErrnoException, files: string[]) => void): void;
|
||||
export function readdirSync(path: string): string[];
|
||||
export function close(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function closeSync(fd: number): void;
|
||||
export function open(path: string, flags: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void;
|
||||
export function open(path: string, flags: string, mode: number, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void;
|
||||
export function open(path: string, flags: string, mode: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void;
|
||||
export function openSync(path: string, flags: string, mode?: number): number;
|
||||
export function openSync(path: string, flags: string, mode?: string): number;
|
||||
export function utimes(path: string, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function utimes(path: string, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function utimesSync(path: string, atime: number, mtime: number): void;
|
||||
export function utimesSync(path: string, atime: Date, mtime: Date): void;
|
||||
export function futimes(fd: number, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function futimes(fd: number, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function futimesSync(fd: number, atime: number, mtime: number): void;
|
||||
export function futimesSync(fd: number, atime: Date, mtime: Date): void;
|
||||
export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
|
||||
export function fsyncSync(fd: number): void;
|
||||
export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void;
|
||||
export function writeSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number;
|
||||
export function read(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: Buffer) => void): void;
|
||||
export function readSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number;
|
||||
export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void;
|
||||
export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void;
|
||||
export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void;
|
||||
export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void;
|
||||
export function readFileSync(filename: string, encoding: string): string;
|
||||
export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string;
|
||||
export function readFileSync(filename: string, options?: { flag?: string; }): Buffer;
|
||||
export function writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void;
|
||||
export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
|
||||
export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
|
||||
export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void;
|
||||
export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void;
|
||||
export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
|
||||
export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
|
||||
export function appendFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void;
|
||||
export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void;
|
||||
export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void;
|
||||
export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void;
|
||||
export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: (curr: Stats, prev: Stats) => void): void;
|
||||
export function unwatchFile(filename: string, listener?: (curr: Stats, prev: Stats) => void): void;
|
||||
export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher;
|
||||
export function watch(filename: string, options: { persistent?: boolean; }, listener?: (event: string, filename: string) => any): FSWatcher;
|
||||
export function exists(path: string, callback?: (exists: boolean) => void): void;
|
||||
export function existsSync(path: string): boolean;
|
||||
export function createReadStream(path: string, options?: {
|
||||
flags?: string;
|
||||
encoding?: string;
|
||||
fd?: string;
|
||||
mode?: number;
|
||||
bufferSize?: number;
|
||||
}): ReadStream;
|
||||
export function createReadStream(path: string, options?: {
|
||||
flags?: string;
|
||||
encoding?: string;
|
||||
fd?: string;
|
||||
mode?: string;
|
||||
bufferSize?: number;
|
||||
}): ReadStream;
|
||||
export function createWriteStream(path: string, options?: {
|
||||
flags?: string;
|
||||
encoding?: string;
|
||||
string?: string;
|
||||
}): WriteStream;
|
||||
}
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
module path {
|
||||
export function normalize(p: string): string;
|
||||
export function join(...paths: any[]): string;
|
||||
export function resolve(...pathSegments: any[]): string;
|
||||
export function relative(from: string, to: string): string;
|
||||
export function dirname(p: string): string;
|
||||
export function basename(p: string, ext?: string): string;
|
||||
export function extname(p: string): string;
|
||||
export var sep: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
module _debugger {
|
||||
export interface Packet {
|
||||
raw: string;
|
||||
headers: string[];
|
||||
body: Message;
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
seq: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface RequestInfo {
|
||||
command: string;
|
||||
arguments: any;
|
||||
}
|
||||
|
||||
export interface Request extends Message, RequestInfo {
|
||||
}
|
||||
|
||||
export interface Event extends Message {
|
||||
event: string;
|
||||
body?: any;
|
||||
}
|
||||
|
||||
export interface Response extends Message {
|
||||
request_seq: number;
|
||||
success: boolean;
|
||||
/** Contains error message if success == false. */
|
||||
message?: string;
|
||||
/** Contains message body if success == true. */
|
||||
body?: any;
|
||||
}
|
||||
|
||||
export interface BreakpointMessageBody {
|
||||
type: string;
|
||||
target: number;
|
||||
line: number;
|
||||
}
|
||||
|
||||
export class Protocol {
|
||||
res: Packet;
|
||||
state: string;
|
||||
execute(data: string): void;
|
||||
serialize(rq: Request): string;
|
||||
onResponse: (pkt: Packet) => void;
|
||||
}
|
||||
|
||||
export var NO_FRAME: number;
|
||||
export var port: number;
|
||||
|
||||
export interface ScriptDesc {
|
||||
name: string;
|
||||
id: number;
|
||||
isNative?: boolean;
|
||||
handle?: number;
|
||||
type: string;
|
||||
lineOffset?: number;
|
||||
columnOffset?: number;
|
||||
lineCount?: number;
|
||||
}
|
||||
|
||||
export interface Breakpoint {
|
||||
id: number;
|
||||
scriptId: number;
|
||||
script: ScriptDesc;
|
||||
line: number;
|
||||
condition?: string;
|
||||
scriptReq?: string;
|
||||
}
|
||||
|
||||
export interface RequestHandler {
|
||||
(err: boolean, body: Message, res: Packet): void;
|
||||
request_seq?: number;
|
||||
}
|
||||
|
||||
export interface ResponseBodyHandler {
|
||||
(err: boolean, body?: any): void;
|
||||
request_seq?: number;
|
||||
}
|
||||
|
||||
export interface ExceptionInfo {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface BreakResponse {
|
||||
script?: ScriptDesc;
|
||||
exception?: ExceptionInfo;
|
||||
sourceLine: number;
|
||||
sourceLineText: string;
|
||||
sourceColumn: number;
|
||||
}
|
||||
|
||||
export function SourceInfo(body: BreakResponse): string;
|
||||
|
||||
export class Client extends events.EventEmitter {
|
||||
protocol: Protocol;
|
||||
scripts: ScriptDesc[];
|
||||
handles: ScriptDesc[];
|
||||
breakpoints: Breakpoint[];
|
||||
currentSourceLine: number;
|
||||
currentSourceColumn: number;
|
||||
currentSourceLineText: string;
|
||||
currentFrame: number;
|
||||
currentScript: string;
|
||||
|
||||
connect(port: number, host: string): void;
|
||||
req(req: any, cb: RequestHandler): void;
|
||||
reqFrameEval(code: string, frame: number, cb: RequestHandler): void;
|
||||
mirrorObject(obj: any, depth: number, cb: ResponseBodyHandler): void;
|
||||
setBreakpoint(rq: BreakpointMessageBody, cb: RequestHandler): void;
|
||||
clearBreakpoint(rq: Request, cb: RequestHandler): void;
|
||||
listbreakpoints(cb: RequestHandler): void;
|
||||
reqSource(from: number, to: number, cb: RequestHandler): void;
|
||||
reqScripts(cb: any): void;
|
||||
reqContinue(cb: RequestHandler): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+823
@@ -0,0 +1,823 @@
|
||||
/**
|
||||
* Declaration module describing the TypeScript Server protocol
|
||||
*/
|
||||
declare module ts.server.protocol {
|
||||
/**
|
||||
* A TypeScript Server message
|
||||
*/
|
||||
export interface Message {
|
||||
/**
|
||||
* Sequence number of the message
|
||||
*/
|
||||
seq: number;
|
||||
|
||||
/**
|
||||
* One of "request", "response", or "event"
|
||||
*/
|
||||
type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-initiated request message
|
||||
*/
|
||||
export interface Request extends Message {
|
||||
/**
|
||||
* The command to execute
|
||||
*/
|
||||
command: string;
|
||||
|
||||
/**
|
||||
* Object containing arguments for the command
|
||||
*/
|
||||
arguments?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-initiated event message
|
||||
*/
|
||||
export interface Event extends Message {
|
||||
/**
|
||||
* Name of event
|
||||
*/
|
||||
event: string;
|
||||
|
||||
/**
|
||||
* Event-specific information
|
||||
*/
|
||||
body?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response by server to client request message.
|
||||
*/
|
||||
export interface Response extends Message {
|
||||
/**
|
||||
* Sequence number of the request message.
|
||||
*/
|
||||
request_seq: number;
|
||||
|
||||
/**
|
||||
* Outcome of the request.
|
||||
*/
|
||||
success: boolean;
|
||||
|
||||
/**
|
||||
* The command requested.
|
||||
*/
|
||||
command: string;
|
||||
|
||||
/**
|
||||
* Contains error message if success == false.
|
||||
*/
|
||||
message?: string;
|
||||
|
||||
/**
|
||||
* Contains message body if success == true.
|
||||
*/
|
||||
body?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for FileRequest messages.
|
||||
*/
|
||||
export interface FileRequestArgs {
|
||||
/**
|
||||
* The file for the request (absolute pathname required).
|
||||
*/
|
||||
file: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request whose sole parameter is a file name.
|
||||
*/
|
||||
export interface FileRequest extends Request {
|
||||
arguments: FileRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of this interface specify a location in a source file:
|
||||
* (file, line, col), where line and column are 1-based.
|
||||
*/
|
||||
export interface FileLocationRequestArgs extends FileRequestArgs {
|
||||
/**
|
||||
* The line number for the request (1-based).
|
||||
*/
|
||||
line: number;
|
||||
|
||||
/**
|
||||
* The column for the request (1-based).
|
||||
*/
|
||||
col: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A request whose arguments specify a file location (file, line, col).
|
||||
*/
|
||||
export interface FileLocationRequest extends FileRequest {
|
||||
arguments: FileLocationRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to definition request; value of command field is
|
||||
* "definition". Return response giving the file locations that
|
||||
* define the symbol found in file at location line, col.
|
||||
*/
|
||||
export interface DefinitionRequest extends FileLocationRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Location in source code expressed as (one-based) line and column.
|
||||
*/
|
||||
export interface Location {
|
||||
line: number;
|
||||
col: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object found in response messages defining a span of text in source code.
|
||||
*/
|
||||
export interface TextSpan {
|
||||
/**
|
||||
* First character of the definition.
|
||||
*/
|
||||
start: Location;
|
||||
|
||||
/**
|
||||
* One character past last character of the definition.
|
||||
*/
|
||||
end: Location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object found in response messages defining a span of text in a specific source file.
|
||||
*/
|
||||
export interface FileSpan extends TextSpan {
|
||||
/**
|
||||
* File containing text span.
|
||||
*/
|
||||
file: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition response message. Gives text range for definition.
|
||||
*/
|
||||
export interface DefinitionResponse extends Response {
|
||||
body?: FileSpan[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find references request; value of command field is
|
||||
* "references". Return response giving the file locations that
|
||||
* reference the symbol found in file at location line, col.
|
||||
*/
|
||||
export interface ReferencesRequest extends FileLocationRequest {
|
||||
}
|
||||
|
||||
export interface ReferencesResponseItem extends FileSpan {
|
||||
/** Text of line containing the reference. Including this
|
||||
* with the response avoids latency of editor loading files
|
||||
* to show text of reference line (the server already has
|
||||
* loaded the referencing files).
|
||||
*/
|
||||
lineText: string;
|
||||
|
||||
/**
|
||||
* True if reference is a write location, false otherwise.
|
||||
*/
|
||||
isWriteAccess: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The body of a "references" response message.
|
||||
*/
|
||||
export interface ReferencesResponseBody {
|
||||
/**
|
||||
* The file locations referencing the symbol.
|
||||
*/
|
||||
refs: ReferencesResponseItem[];
|
||||
|
||||
/**
|
||||
* The name of the symbol.
|
||||
*/
|
||||
symbolName: string;
|
||||
|
||||
/**
|
||||
* The start column of the symbol (on the line provided by the references request).
|
||||
*/
|
||||
symbolStartCol: number;
|
||||
|
||||
/**
|
||||
* The full display name of the symbol.
|
||||
*/
|
||||
symbolDisplayString: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response to "references" request.
|
||||
*/
|
||||
export interface ReferencesResponse extends Response {
|
||||
body?: ReferencesResponseBody;
|
||||
}
|
||||
|
||||
export interface RenameRequestArgs extends FileLocationRequestArgs {
|
||||
findInComments?: boolean;
|
||||
findInStrings?: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rename request; value of command field is "rename". Return
|
||||
* response giving the file locations that reference the symbol
|
||||
* found in file at location line, col. Also return full display
|
||||
* name of the symbol so that client can print it unambiguously.
|
||||
*/
|
||||
export interface RenameRequest extends FileLocationRequest {
|
||||
arguments: RenameRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about the item to be renamed.
|
||||
*/
|
||||
export interface RenameInfo {
|
||||
/**
|
||||
* True if item can be renamed.
|
||||
*/
|
||||
canRename: boolean;
|
||||
|
||||
/**
|
||||
* Error message if item can not be renamed.
|
||||
*/
|
||||
localizedErrorMessage?: string;
|
||||
|
||||
/**
|
||||
* Display name of the item to be renamed.
|
||||
*/
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* Full display name of item to be renamed.
|
||||
*/
|
||||
fullDisplayName: string;
|
||||
|
||||
/**
|
||||
* The items's kind (such as 'className' or 'parameterName' or plain 'text').
|
||||
*/
|
||||
kind: string;
|
||||
|
||||
/**
|
||||
* Optional modifiers for the kind (such as 'public').
|
||||
*/
|
||||
kindModifiers: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of text spans, all in 'file'.
|
||||
*/
|
||||
export interface SpanGroup {
|
||||
/** The file to which the spans apply */
|
||||
file: string;
|
||||
/** The text spans in this group */
|
||||
locs: TextSpan[];
|
||||
}
|
||||
|
||||
export interface RenameResponseBody {
|
||||
/**
|
||||
* Information about the item to be renamed.
|
||||
*/
|
||||
info: RenameInfo;
|
||||
|
||||
/**
|
||||
* An array of span groups (one per file) that refer to the item to be renamed.
|
||||
*/
|
||||
locs: SpanGroup[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename response message.
|
||||
*/
|
||||
export interface RenameResponse extends Response {
|
||||
body?: RenameResponseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open request; value of command field is "open". Notify the
|
||||
* server that the client has file open. The server will not
|
||||
* monitor the filesystem for changes in this file and will assume
|
||||
* that the client is updating the server (using the change and/or
|
||||
* reload messages) when the file changes. Server does not currently
|
||||
* send a response to an open request.
|
||||
*/
|
||||
export interface OpenRequest extends FileRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Close request; value of command field is "close". Notify the
|
||||
* server that the client has closed a previously open file. If
|
||||
* file is still referenced by open files, the server will resume
|
||||
* monitoring the filesystem for changes to file. Server does not
|
||||
* currently send a response to a close request.
|
||||
*/
|
||||
export interface CloseRequest extends FileRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickinfo request; value of command field is
|
||||
* "quickinfo". Return response giving a quick type and
|
||||
* documentation string for the symbol found in file at location
|
||||
* line, col.
|
||||
*/
|
||||
export interface QuickInfoRequest extends FileLocationRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Body of QuickInfoResponse.
|
||||
*/
|
||||
export interface QuickInfoResponseBody {
|
||||
/**
|
||||
* The symbol's kind (such as 'className' or 'parameterName' or plain 'text').
|
||||
*/
|
||||
kind: string;
|
||||
|
||||
/**
|
||||
* Optional modifiers for the kind (such as 'public').
|
||||
*/
|
||||
kindModifiers: string;
|
||||
|
||||
/**
|
||||
* Starting file location of symbol.
|
||||
*/
|
||||
start: Location;
|
||||
|
||||
/**
|
||||
* One past last character of symbol.
|
||||
*/
|
||||
end: Location;
|
||||
|
||||
/**
|
||||
* Type and kind of symbol.
|
||||
*/
|
||||
displayString: string;
|
||||
|
||||
/**
|
||||
* Documentation associated with symbol.
|
||||
*/
|
||||
documentation: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickinfo response message.
|
||||
*/
|
||||
export interface QuickInfoResponse extends Response {
|
||||
body?: QuickInfoResponseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for format messages.
|
||||
*/
|
||||
export interface FormatRequestArgs extends FileLocationRequestArgs {
|
||||
/**
|
||||
* Last line of range for which to format text in file.
|
||||
*/
|
||||
endLine: number;
|
||||
|
||||
/**
|
||||
* Last column of range for which to format text in file.
|
||||
*/
|
||||
endCol: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format request; value of command field is "format". Return
|
||||
* response giving zero or more edit instructions. The edit
|
||||
* instructions will be sorted in file order. Applying the edit
|
||||
* instructions in reverse to file will result in correctly
|
||||
* reformatted text.
|
||||
*/
|
||||
export interface FormatRequest extends FileLocationRequest {
|
||||
arguments: FormatRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object found in response messages defining an editing
|
||||
* instruction for a span of text in source code. The effect of
|
||||
* this instruction is to replace the text starting at start and
|
||||
* ending one character before end with newText. For an insertion,
|
||||
* the text span is empty. For a deletion, newText is empty.
|
||||
*/
|
||||
export interface CodeEdit {
|
||||
/**
|
||||
* First character of the text span to edit.
|
||||
*/
|
||||
start: Location;
|
||||
|
||||
/**
|
||||
* One character past last character of the text span to edit.
|
||||
*/
|
||||
end: Location;
|
||||
|
||||
/**
|
||||
* Replace the span defined above with this string (may be
|
||||
* the empty string).
|
||||
*/
|
||||
newText: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and format on key response message.
|
||||
*/
|
||||
export interface FormatResponse extends Response {
|
||||
body?: CodeEdit[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for format on key messages.
|
||||
*/
|
||||
export interface FormatOnKeyRequestArgs extends FileLocationRequestArgs {
|
||||
/**
|
||||
* Key pressed (';', '\n', or '}').
|
||||
*/
|
||||
key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format on key request; value of command field is
|
||||
* "formatonkey". Given file location and key typed (as string),
|
||||
* return response giving zero or more edit instructions. The
|
||||
* edit instructions will be sorted in file order. Applying the
|
||||
* edit instructions in reverse to file will result in correctly
|
||||
* reformatted text.
|
||||
*/
|
||||
export interface FormatOnKeyRequest extends FileLocationRequest {
|
||||
arguments: FormatOnKeyRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for completions messages.
|
||||
*/
|
||||
export interface CompletionsRequestArgs extends FileLocationRequestArgs {
|
||||
/**
|
||||
* Optional prefix to apply to possible completions.
|
||||
*/
|
||||
prefix?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completions request; value of command field is "completions".
|
||||
* Given a file location (file, line, col) and a prefix (which may
|
||||
* be the empty string), return the possible completions that
|
||||
* begin with prefix.
|
||||
*/
|
||||
export interface CompletionsRequest extends FileLocationRequest {
|
||||
arguments: CompletionsRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for completion details request.
|
||||
*/
|
||||
export interface CompletionDetailsRequestArgs extends FileLocationRequestArgs {
|
||||
/**
|
||||
* Names of one or more entries for which to obtain details.
|
||||
*/
|
||||
entryNames: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Completion entry details request; value of command field is
|
||||
* "completionEntryDetails". Given a file location (file, line,
|
||||
* col) and an array of completion entry names return more
|
||||
* detailed information for each completion entry.
|
||||
*/
|
||||
export interface CompletionDetailsRequest extends FileLocationRequest {
|
||||
arguments: CompletionDetailsRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of a symbol description.
|
||||
*/
|
||||
export interface SymbolDisplayPart {
|
||||
/**
|
||||
* Text of an item describing the symbol.
|
||||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* The symbol's kind (such as 'className' or 'parameterName' or plain 'text').
|
||||
*/
|
||||
kind: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An item found in a completion response.
|
||||
*/
|
||||
export interface CompletionEntry {
|
||||
/**
|
||||
* The symbol's name.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The symbol's kind (such as 'className' or 'parameterName').
|
||||
*/
|
||||
kind: string;
|
||||
/**
|
||||
* Optional modifiers for the kind (such as 'public').
|
||||
*/
|
||||
kindModifiers: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional completion entry details, available on demand
|
||||
*/
|
||||
export interface CompletionEntryDetails extends CompletionEntry {
|
||||
/**
|
||||
* Display parts of the symbol (similar to quick info).
|
||||
*/
|
||||
displayParts: SymbolDisplayPart[];
|
||||
|
||||
/**
|
||||
* Documentation strings for the symbol.
|
||||
*/
|
||||
documentation: SymbolDisplayPart[];
|
||||
}
|
||||
|
||||
export interface CompletionsResponse extends Response {
|
||||
body?: CompletionEntry[];
|
||||
}
|
||||
|
||||
export interface CompletionDetailsResponse extends Response {
|
||||
body?: CompletionEntryDetails[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for geterr messages.
|
||||
*/
|
||||
export interface GeterrRequestArgs {
|
||||
/**
|
||||
* List of file names for which to compute compiler errors.
|
||||
* The files will be checked in list order.
|
||||
*/
|
||||
files: string[];
|
||||
|
||||
/**
|
||||
* Delay in milliseconds to wait before starting to compute
|
||||
* errors for the files in the file list
|
||||
*/
|
||||
delay: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Geterr request; value of command field is "geterr". Wait for
|
||||
* delay milliseconds and then, if during the wait no change or
|
||||
* reload messages have arrived for the first file in the files
|
||||
* list, get the syntactic errors for the file, field requests,
|
||||
* and then get the semantic errors for the file. Repeat with a
|
||||
* smaller delay for each subsequent file on the files list. Best
|
||||
* practice for an editor is to send a file list containing each
|
||||
* file that is currently visible, in most-recently-used order.
|
||||
*/
|
||||
export interface GeterrRequest extends Request {
|
||||
arguments: GeterrRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Item of diagnostic information found in a DiagnosticEvent message.
|
||||
*/
|
||||
export interface Diagnostic {
|
||||
/**
|
||||
* Starting file location at which text appies.
|
||||
*/
|
||||
start: Location;
|
||||
|
||||
/**
|
||||
* The last file location at which the text applies.
|
||||
*/
|
||||
end: Location;
|
||||
|
||||
/**
|
||||
* Text of diagnostic message.
|
||||
*/
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticEventBody {
|
||||
/**
|
||||
* The file for which diagnostic information is reported.
|
||||
*/
|
||||
file: string;
|
||||
|
||||
/**
|
||||
* An array of diagnostic information items.
|
||||
*/
|
||||
diagnostics: Diagnostic[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Event message for "syntaxDiag" and "semanticDiag" event types.
|
||||
* These events provide syntactic and semantic errors for a file.
|
||||
*/
|
||||
export interface DiagnosticEvent extends Event {
|
||||
body?: DiagnosticEventBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for reload request.
|
||||
*/
|
||||
export interface ReloadRequestArgs extends FileRequestArgs {
|
||||
/**
|
||||
* Name of temporary file from which to reload file
|
||||
* contents. May be same as file.
|
||||
*/
|
||||
tmpfile: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload request message; value of command field is "reload".
|
||||
* Reload contents of file with name given by the 'file' argument
|
||||
* from temporary file with name given by the 'tmpfile' argument.
|
||||
* The two names can be identical.
|
||||
*/
|
||||
export interface ReloadRequest extends FileRequest {
|
||||
arguments: ReloadRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response to "reload" request. This is just an acknowledgement, so
|
||||
* no body field is required.
|
||||
*/
|
||||
export interface ReloadResponse extends Response {
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for saveto request.
|
||||
*/
|
||||
export interface SavetoRequestArgs extends FileRequestArgs {
|
||||
/**
|
||||
* Name of temporary file into which to save server's view of
|
||||
* file contents.
|
||||
*/
|
||||
tmpfile: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saveto request message; value of command field is "saveto".
|
||||
* For debugging purposes, save to a temporaryfile (named by
|
||||
* argument 'tmpfile') the contents of file named by argument
|
||||
* 'file'. The server does not currently send a response to a
|
||||
* "saveto" request.
|
||||
*/
|
||||
export interface SavetoRequest extends FileRequest {
|
||||
arguments: SavetoRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for navto request message.
|
||||
*/
|
||||
export interface NavtoRequestArgs extends FileRequestArgs {
|
||||
/**
|
||||
* Search term to navigate to from current location; term can
|
||||
* be '.*' or an identifier prefix.
|
||||
*/
|
||||
searchTerm: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navto request message; value of command field is "navto".
|
||||
* Return list of objects giving file locations and symbols that
|
||||
* match the search term given in argument 'searchTerm'. The
|
||||
* context for the search is given by the named file.
|
||||
*/
|
||||
export interface NavtoRequest extends FileRequest {
|
||||
arguments: NavtoRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* An item found in a navto response.
|
||||
*/
|
||||
export interface NavtoItem {
|
||||
/**
|
||||
* The symbol's name.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The symbol's kind (such as 'className' or 'parameterName').
|
||||
*/
|
||||
kind: string;
|
||||
|
||||
/**
|
||||
* exact, substring, or prefix.
|
||||
*/
|
||||
matchKind?: string;
|
||||
|
||||
/**
|
||||
* Optional modifiers for the kind (such as 'public').
|
||||
*/
|
||||
kindModifiers?: string;
|
||||
|
||||
/**
|
||||
* The file in which the symbol is found.
|
||||
*/
|
||||
file: string;
|
||||
|
||||
/**
|
||||
* The location within file at which the symbol is found.
|
||||
*/
|
||||
start: Location;
|
||||
|
||||
/**
|
||||
* One past the last character of the symbol.
|
||||
*/
|
||||
end: Location;
|
||||
|
||||
/**
|
||||
* Name of symbol's container symbol (if any); for example,
|
||||
* the class name if symbol is a class member.
|
||||
*/
|
||||
containerName?: string;
|
||||
|
||||
/**
|
||||
* Kind of symbol's container symbol (if any).
|
||||
*/
|
||||
containerKind?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navto response message. Body is an array of navto items. Each
|
||||
* item gives a symbol that matched the search term.
|
||||
*/
|
||||
export interface NavtoResponse extends Response {
|
||||
body?: NavtoItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for change request message.
|
||||
*/
|
||||
export interface ChangeRequestArgs extends FormatRequestArgs {
|
||||
/**
|
||||
* Optional string to insert at location (file, line, col).
|
||||
*/
|
||||
insertString?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change request message; value of command field is "change".
|
||||
* Update the server's view of the file named by argument 'file'.
|
||||
* Server does not currently send a response to a change request.
|
||||
*/
|
||||
export interface ChangeRequest extends FileLocationRequest {
|
||||
arguments: ChangeRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response to "brace" request.
|
||||
*/
|
||||
export interface BraceResponse extends Response {
|
||||
body?: TextSpan[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Brace matching request; value of command field is "brace".
|
||||
* Return response giving the file locations of matching braces
|
||||
* found in file at location line, col.
|
||||
*/
|
||||
export interface BraceRequest extends FileLocationRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* NavBar itesm request; value of command field is "navbar".
|
||||
* Return response giving the list of navigation bar entries
|
||||
* extracted from the requested file.
|
||||
*/
|
||||
export interface NavBarRequest extends FileRequest {
|
||||
}
|
||||
|
||||
export interface NavigationBarItem {
|
||||
/**
|
||||
* The item's display text.
|
||||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* The symbol's kind (such as 'className' or 'parameterName').
|
||||
*/
|
||||
kind: string;
|
||||
|
||||
/**
|
||||
* Optional modifiers for the kind (such as 'public').
|
||||
*/
|
||||
kindModifiers?: string;
|
||||
|
||||
/**
|
||||
* The definition locations of the item.
|
||||
*/
|
||||
spans: TextSpan[];
|
||||
|
||||
/**
|
||||
* Optional children.
|
||||
*/
|
||||
childItems?: NavigationBarItem[];
|
||||
}
|
||||
|
||||
export interface NavBarResponse extends Response {
|
||||
body?: NavigationBarItem[];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/// <reference path="node.d.ts" />
|
||||
/// <reference path="session.ts" />
|
||||
|
||||
module ts.server {
|
||||
var nodeproto: typeof NodeJS._debugger = require('_debugger');
|
||||
var readline: NodeJS.ReadLine = require('readline');
|
||||
var path: NodeJS.Path = require('path');
|
||||
var fs: typeof NodeJS.fs = require('fs');
|
||||
|
||||
var rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
terminal: false,
|
||||
});
|
||||
|
||||
class Logger implements ts.server.Logger {
|
||||
fd = -1;
|
||||
seq = 0;
|
||||
inGroup = false;
|
||||
firstInGroup = true;
|
||||
|
||||
constructor(public logFilename: string) {
|
||||
}
|
||||
|
||||
static padStringRight(str: string, padding: string) {
|
||||
return (str + padding).slice(0, padding.length);
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.fd >= 0) {
|
||||
fs.close(this.fd);
|
||||
}
|
||||
}
|
||||
|
||||
perftrc(s: string) {
|
||||
this.msg(s, "Perf");
|
||||
}
|
||||
|
||||
info(s: string) {
|
||||
this.msg(s, "Info");
|
||||
}
|
||||
|
||||
startGroup() {
|
||||
this.inGroup = true;
|
||||
this.firstInGroup = true;
|
||||
}
|
||||
|
||||
endGroup() {
|
||||
this.inGroup = false;
|
||||
this.seq++;
|
||||
this.firstInGroup = true;
|
||||
}
|
||||
|
||||
msg(s: string, type = "Err") {
|
||||
if (this.fd < 0) {
|
||||
this.fd = fs.openSync(this.logFilename, "w");
|
||||
}
|
||||
if (this.fd >= 0) {
|
||||
s = s + "\n";
|
||||
var prefix = Logger.padStringRight(type + " " + this.seq.toString(), " ");
|
||||
if (this.firstInGroup) {
|
||||
s = prefix + s;
|
||||
this.firstInGroup = false;
|
||||
}
|
||||
if (!this.inGroup) {
|
||||
this.seq++;
|
||||
this.firstInGroup = true;
|
||||
}
|
||||
var buf = new Buffer(s);
|
||||
fs.writeSync(this.fd, buf, 0, buf.length, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface WatchedFile {
|
||||
fileName: string;
|
||||
callback: (fileName: string) => void;
|
||||
mtime: Date;
|
||||
}
|
||||
|
||||
class WatchedFileSet {
|
||||
private watchedFiles: WatchedFile[] = [];
|
||||
private nextFileToCheck = 0;
|
||||
private watchTimer: NodeJS.Timer;
|
||||
private static fileDeleted = 34;
|
||||
|
||||
// average async stat takes about 30 microseconds
|
||||
// set chunk size to do 30 files in < 1 millisecond
|
||||
constructor(public interval = 2500, public chunkSize = 30) {
|
||||
}
|
||||
|
||||
private static copyListRemovingItem<T>(item: T, list: T[]) {
|
||||
var copiedList: T[] = [];
|
||||
for (var i = 0, len = list.length; i < len; i++) {
|
||||
if (list[i] != item) {
|
||||
copiedList.push(list[i]);
|
||||
}
|
||||
}
|
||||
return copiedList;
|
||||
}
|
||||
|
||||
private static getModifiedTime(fileName: string): Date {
|
||||
return fs.statSync(fileName).mtime;
|
||||
}
|
||||
|
||||
private poll(checkedIndex: number) {
|
||||
var watchedFile = this.watchedFiles[checkedIndex];
|
||||
if (!watchedFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
fs.stat(watchedFile.fileName,(err, stats) => {
|
||||
if (err) {
|
||||
var msg = err.message;
|
||||
if (err.errno) {
|
||||
msg += " errno: " + err.errno.toString();
|
||||
}
|
||||
if (err.errno == WatchedFileSet.fileDeleted) {
|
||||
watchedFile.callback(watchedFile.fileName);
|
||||
}
|
||||
}
|
||||
else if (watchedFile.mtime.getTime() != stats.mtime.getTime()) {
|
||||
watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName);
|
||||
watchedFile.callback(watchedFile.fileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// this implementation uses polling and
|
||||
// stat due to inconsistencies of fs.watch
|
||||
// and efficiency of stat on modern filesystems
|
||||
private startWatchTimer() {
|
||||
this.watchTimer = setInterval(() => {
|
||||
var count = 0;
|
||||
var nextToCheck = this.nextFileToCheck;
|
||||
var firstCheck = -1;
|
||||
while ((count < this.chunkSize) && (nextToCheck != firstCheck)) {
|
||||
this.poll(nextToCheck);
|
||||
if (firstCheck < 0) {
|
||||
firstCheck = nextToCheck;
|
||||
}
|
||||
nextToCheck++;
|
||||
if (nextToCheck === this.watchedFiles.length) {
|
||||
nextToCheck = 0;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
this.nextFileToCheck = nextToCheck;
|
||||
}, this.interval);
|
||||
}
|
||||
|
||||
addFile(fileName: string, callback: (fileName: string) => void ): WatchedFile {
|
||||
var file: WatchedFile = {
|
||||
fileName,
|
||||
callback,
|
||||
mtime: WatchedFileSet.getModifiedTime(fileName)
|
||||
};
|
||||
|
||||
this.watchedFiles.push(file);
|
||||
if (this.watchedFiles.length === 1) {
|
||||
this.startWatchTimer();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
removeFile(file: WatchedFile) {
|
||||
this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles);
|
||||
}
|
||||
}
|
||||
|
||||
class IOSession extends Session {
|
||||
constructor(host: ServerHost, logger: ts.server.Logger) {
|
||||
super(host, logger);
|
||||
}
|
||||
|
||||
listen() {
|
||||
rl.on('line',(input: string) => {
|
||||
var message = input.trim();
|
||||
this.onMessage(message);
|
||||
});
|
||||
|
||||
rl.on('close',() => {
|
||||
this.projectService.closeLog();
|
||||
this.projectService.log("Exiting...");
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// This places log file in the directory containing editorServices.js
|
||||
// TODO: check that this location is writable
|
||||
var logger = new Logger(__dirname + "/.log" + process.pid.toString());
|
||||
|
||||
|
||||
// REVIEW: for now this implementation uses polling.
|
||||
// The advantage of polling is that it works reliably
|
||||
// on all os and with network mounted files.
|
||||
// For 90 referenced files, the average time to detect
|
||||
// changes is 2*msInterval (by default 5 seconds).
|
||||
// The overhead of this is .04 percent (1/2500) with
|
||||
// average pause of < 1 millisecond (and max
|
||||
// pause less than 1.5 milliseconds); question is
|
||||
// do we anticipate reference sets in the 100s and
|
||||
// do we care about waiting 10-20 seconds to detect
|
||||
// changes for large reference sets? If so, do we want
|
||||
// to increase the chunk size or decrease the interval
|
||||
// time dynamically to match the large reference set?
|
||||
var watchedFileSet = new WatchedFileSet();
|
||||
ts.sys.watchFile = function (fileName, callback) {
|
||||
var watchedFile = watchedFileSet.addFile(fileName, callback);
|
||||
return {
|
||||
close: () => watchedFileSet.removeFile(watchedFile)
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Start listening
|
||||
new IOSession(ts.sys, logger).listen();
|
||||
}
|
||||
@@ -0,0 +1,801 @@
|
||||
/// <reference path="..\compiler\commandLineParser.ts" />
|
||||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="node.d.ts" />
|
||||
/// <reference path="protocol.d.ts" />
|
||||
/// <reference path="editorServices.ts" />
|
||||
|
||||
module ts.server {
|
||||
var spaceCache = [" ", " ", " ", " "];
|
||||
|
||||
interface StackTraceError extends Error {
|
||||
stack?: string;
|
||||
}
|
||||
|
||||
function generateSpaces(n: number): string {
|
||||
if (!spaceCache[n]) {
|
||||
var strBuilder = "";
|
||||
for (var i = 0; i < n; i++) {
|
||||
strBuilder += " ";
|
||||
}
|
||||
spaceCache[n] = strBuilder;
|
||||
}
|
||||
return spaceCache[n];
|
||||
}
|
||||
|
||||
interface FileStart {
|
||||
file: string;
|
||||
start: ILineInfo;
|
||||
}
|
||||
|
||||
function compareNumber(a: number, b: number) {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
else if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
else return 1;
|
||||
}
|
||||
|
||||
function compareFileStart(a: FileStart, b: FileStart) {
|
||||
if (a.file < b.file) {
|
||||
return -1;
|
||||
}
|
||||
else if (a.file == b.file) {
|
||||
var n = compareNumber(a.start.line, b.start.line);
|
||||
if (n == 0) {
|
||||
return compareNumber(a.start.col, b.start.col);
|
||||
}
|
||||
else return n;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
function sortNavItems(items: ts.NavigateToItem[]) {
|
||||
return items.sort((a, b) => {
|
||||
if (a.matchKind < b.matchKind) {
|
||||
return -1;
|
||||
}
|
||||
else if (a.matchKind == b.matchKind) {
|
||||
var lowa = a.name.toLowerCase();
|
||||
var lowb = b.name.toLowerCase();
|
||||
if (lowa < lowb) {
|
||||
return -1;
|
||||
}
|
||||
else if (lowa == lowb) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function formatDiag(fileName: string, project: Project, diag: ts.Diagnostic) {
|
||||
return {
|
||||
start: project.compilerService.host.positionToLineCol(fileName, diag.start),
|
||||
end: project.compilerService.host.positionToLineCol(fileName, diag.start + diag.length),
|
||||
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n")
|
||||
};
|
||||
}
|
||||
|
||||
interface PendingErrorCheck {
|
||||
fileName: string;
|
||||
project: Project;
|
||||
}
|
||||
|
||||
function allEditsBeforePos(edits: ts.TextChange[], pos: number) {
|
||||
for (var i = 0, len = edits.length; i < len; i++) {
|
||||
if (ts.textSpanEnd(edits[i].span) >= pos) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export module CommandNames {
|
||||
export var Change = "change";
|
||||
export var Close = "close";
|
||||
export var Completions = "completions";
|
||||
export var CompletionDetails = "completionEntryDetails";
|
||||
export var Definition = "definition";
|
||||
export var Format = "format";
|
||||
export var Formatonkey = "formatonkey";
|
||||
export var Geterr = "geterr";
|
||||
export var NavBar = "navbar";
|
||||
export var Navto = "navto";
|
||||
export var Open = "open";
|
||||
export var Quickinfo = "quickinfo";
|
||||
export var References = "references";
|
||||
export var Reload = "reload";
|
||||
export var Rename = "rename";
|
||||
export var Saveto = "saveto";
|
||||
export var Brace = "brace";
|
||||
export var Unknown = "unknown";
|
||||
}
|
||||
|
||||
module Errors {
|
||||
export var NoProject = new Error("No Project.");
|
||||
export var NoContent = new Error("No Content.");
|
||||
}
|
||||
|
||||
export interface ServerHost extends ts.System {
|
||||
}
|
||||
|
||||
export class Session {
|
||||
projectService: ProjectService;
|
||||
pendingOperation = false;
|
||||
fileHash: ts.Map<number> = {};
|
||||
nextFileId = 1;
|
||||
errorTimer: NodeJS.Timer;
|
||||
immediateId: any;
|
||||
changeSeq = 0;
|
||||
|
||||
constructor(private host: ServerHost, private logger: Logger) {
|
||||
this.projectService = new ProjectService(host, logger);
|
||||
}
|
||||
|
||||
logError(err: Error, cmd: string) {
|
||||
var typedErr = <StackTraceError>err;
|
||||
var msg = "Exception on executing command " + cmd;
|
||||
if (typedErr.message) {
|
||||
msg += ":\n" + typedErr.message;
|
||||
if (typedErr.stack) {
|
||||
msg += "\n" + typedErr.stack;
|
||||
}
|
||||
}
|
||||
this.projectService.log(msg);
|
||||
}
|
||||
|
||||
sendLineToClient(line: string) {
|
||||
this.host.write(line + this.host.newLine);
|
||||
}
|
||||
|
||||
send(msg: NodeJS._debugger.Message) {
|
||||
var json = JSON.stringify(msg);
|
||||
this.sendLineToClient('Content-Length: ' + (1 + Buffer.byteLength(json, 'utf8')) +
|
||||
'\r\n\r\n' + json);
|
||||
}
|
||||
|
||||
event(info: any, eventName: string) {
|
||||
var ev: NodeJS._debugger.Event = {
|
||||
seq: 0,
|
||||
type: "event",
|
||||
event: eventName,
|
||||
body: info,
|
||||
};
|
||||
this.send(ev);
|
||||
}
|
||||
|
||||
response(info: any, cmdName: string, reqSeq = 0, errorMsg?: string) {
|
||||
var res: protocol.Response = {
|
||||
seq: 0,
|
||||
type: "response",
|
||||
command: cmdName,
|
||||
request_seq: reqSeq,
|
||||
success: !errorMsg,
|
||||
}
|
||||
if (!errorMsg) {
|
||||
res.body = info;
|
||||
}
|
||||
else {
|
||||
res.message = errorMsg;
|
||||
}
|
||||
this.send(res);
|
||||
}
|
||||
|
||||
output(body: any, commandName: string, requestSequence = 0, errorMessage?: string) {
|
||||
this.response(body, commandName, requestSequence, errorMessage);
|
||||
}
|
||||
|
||||
semanticCheck(file: string, project: Project) {
|
||||
var diags = project.compilerService.languageService.getSemanticDiagnostics(file);
|
||||
if (diags) {
|
||||
var bakedDiags = diags.map((diag) => formatDiag(file, project, diag));
|
||||
this.event({ file: file, diagnostics: bakedDiags }, "semanticDiag");
|
||||
}
|
||||
}
|
||||
|
||||
syntacticCheck(file: string, project: Project) {
|
||||
var diags = project.compilerService.languageService.getSyntacticDiagnostics(file);
|
||||
if (diags) {
|
||||
var bakedDiags = diags.map((diag) => formatDiag(file, project, diag));
|
||||
this.event({ file: file, diagnostics: bakedDiags }, "syntaxDiag");
|
||||
}
|
||||
}
|
||||
|
||||
errorCheck(file: string, project: Project) {
|
||||
this.syntacticCheck(file, project);
|
||||
this.semanticCheck(file, project);
|
||||
}
|
||||
|
||||
updateErrorCheck(checkList: PendingErrorCheck[], seq: number,
|
||||
matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200) {
|
||||
if (followMs > ms) {
|
||||
followMs = ms;
|
||||
}
|
||||
if (this.errorTimer) {
|
||||
clearTimeout(this.errorTimer);
|
||||
}
|
||||
if (this.immediateId) {
|
||||
clearImmediate(this.immediateId);
|
||||
this.immediateId = undefined;
|
||||
}
|
||||
var index = 0;
|
||||
var checkOne = () => {
|
||||
if (matchSeq(seq)) {
|
||||
var checkSpec = checkList[index++];
|
||||
if (checkSpec.project.getSourceFileFromName(checkSpec.fileName)) {
|
||||
this.syntacticCheck(checkSpec.fileName, checkSpec.project);
|
||||
this.immediateId = setImmediate(() => {
|
||||
this.semanticCheck(checkSpec.fileName, checkSpec.project);
|
||||
this.immediateId = undefined;
|
||||
if (checkList.length > index) {
|
||||
this.errorTimer = setTimeout(checkOne, followMs);
|
||||
}
|
||||
else {
|
||||
this.errorTimer = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((checkList.length > index) && (matchSeq(seq))) {
|
||||
this.errorTimer = setTimeout(checkOne, ms);
|
||||
}
|
||||
}
|
||||
|
||||
getDefinition(line: number, col: number, fileName: string): protocol.FileSpan[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
|
||||
var definitions = compilerService.languageService.getDefinitionAtPosition(file, position);
|
||||
if (!definitions) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
return definitions.map(def => ({
|
||||
file: def.fileName,
|
||||
start: compilerService.host.positionToLineCol(def.fileName, def.textSpan.start),
|
||||
end: compilerService.host.positionToLineCol(def.fileName, ts.textSpanEnd(def.textSpan))
|
||||
}));
|
||||
}
|
||||
|
||||
getRenameLocations(line: number, col: number, fileName: string,findInComments: boolean, findInStrings: boolean): protocol.RenameResponseBody {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
var renameInfo = compilerService.languageService.getRenameInfo(file, position);
|
||||
if (!renameInfo) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
if (!renameInfo.canRename) {
|
||||
return {
|
||||
info: renameInfo,
|
||||
locs: []
|
||||
};
|
||||
}
|
||||
|
||||
var renameLocations = compilerService.languageService.findRenameLocations(file, position, findInStrings, findInComments);
|
||||
if (!renameLocations) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
var bakedRenameLocs = renameLocations.map(location => (<protocol.FileSpan>{
|
||||
file: location.fileName,
|
||||
start: compilerService.host.positionToLineCol(location.fileName, location.textSpan.start),
|
||||
end: compilerService.host.positionToLineCol(location.fileName, ts.textSpanEnd(location.textSpan)),
|
||||
})).sort((a, b) => {
|
||||
if (a.file < b.file) {
|
||||
return -1;
|
||||
}
|
||||
else if (a.file > b.file) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// reverse sort assuming no overlap
|
||||
if (a.start.line < b.start.line) {
|
||||
return 1;
|
||||
}
|
||||
else if (a.start.line > b.start.line) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return b.start.col - a.start.col;
|
||||
}
|
||||
}
|
||||
}).reduce<protocol.SpanGroup[]>((accum: protocol.SpanGroup[], cur: protocol.FileSpan) => {
|
||||
var curFileAccum: protocol.SpanGroup;
|
||||
if (accum.length > 0) {
|
||||
curFileAccum = accum[accum.length - 1];
|
||||
if (curFileAccum.file != cur.file) {
|
||||
curFileAccum = undefined;
|
||||
}
|
||||
}
|
||||
if (!curFileAccum) {
|
||||
curFileAccum = { file: cur.file, locs: [] };
|
||||
accum.push(curFileAccum);
|
||||
}
|
||||
curFileAccum.locs.push({ start: cur.start, end: cur.end });
|
||||
return accum;
|
||||
}, []);
|
||||
|
||||
return { info: renameInfo, locs: bakedRenameLocs };
|
||||
}
|
||||
|
||||
getReferences(line: number, col: number, fileName: string): protocol.ReferencesResponseBody {
|
||||
// TODO: get all projects for this file; report refs for all projects deleting duplicates
|
||||
// can avoid duplicates by eliminating same ref file from subsequent projects
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
|
||||
var references = compilerService.languageService.getReferencesAtPosition(file, position);
|
||||
if (!references) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
var nameInfo = compilerService.languageService.getQuickInfoAtPosition(file, position);
|
||||
if (!nameInfo) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
var displayString = ts.displayPartsToString(nameInfo.displayParts);
|
||||
var nameSpan = nameInfo.textSpan;
|
||||
var nameColStart = compilerService.host.positionToLineCol(file, nameSpan.start).col;
|
||||
var nameText = compilerService.host.getScriptSnapshot(file).getText(nameSpan.start, ts.textSpanEnd(nameSpan));
|
||||
var bakedRefs: protocol.ReferencesResponseItem[] = references.map((ref) => {
|
||||
var start = compilerService.host.positionToLineCol(ref.fileName, ref.textSpan.start);
|
||||
var refLineSpan = compilerService.host.lineToTextSpan(ref.fileName, start.line - 1);
|
||||
var snap = compilerService.host.getScriptSnapshot(ref.fileName);
|
||||
var lineText = snap.getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
return {
|
||||
file: ref.fileName,
|
||||
start: start,
|
||||
lineText: lineText,
|
||||
end: compilerService.host.positionToLineCol(ref.fileName, ts.textSpanEnd(ref.textSpan)),
|
||||
isWriteAccess: ref.isWriteAccess
|
||||
};
|
||||
}).sort(compareFileStart);
|
||||
return {
|
||||
refs: bakedRefs,
|
||||
symbolName: nameText,
|
||||
symbolStartCol: nameColStart,
|
||||
symbolDisplayString: displayString
|
||||
};
|
||||
}
|
||||
|
||||
openClientFile(fileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
this.projectService.openClientFile(file);
|
||||
}
|
||||
|
||||
getQuickInfo(line: number, col: number, fileName: string): protocol.QuickInfoResponseBody {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
var quickInfo = compilerService.languageService.getQuickInfoAtPosition(file, position);
|
||||
if (!quickInfo) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
var displayString = ts.displayPartsToString(quickInfo.displayParts);
|
||||
var docString = ts.displayPartsToString(quickInfo.documentation);
|
||||
return {
|
||||
kind: quickInfo.kind,
|
||||
kindModifiers: quickInfo.kindModifiers,
|
||||
start: compilerService.host.positionToLineCol(file, quickInfo.textSpan.start),
|
||||
end: compilerService.host.positionToLineCol(file, ts.textSpanEnd(quickInfo.textSpan)),
|
||||
displayString: displayString,
|
||||
documentation: docString,
|
||||
};
|
||||
}
|
||||
|
||||
getFormattingEditsForRange(line: number, col: number, endLine: number, endCol: number, fileName: string): protocol.CodeEdit[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var startPosition = compilerService.host.lineColToPosition(file, line, col);
|
||||
var endPosition = compilerService.host.lineColToPosition(file, endLine, endCol);
|
||||
|
||||
// TODO: avoid duplicate code (with formatonkey)
|
||||
var edits = compilerService.languageService.getFormattingEditsForRange(file, startPosition, endPosition, compilerService.formatCodeOptions);
|
||||
if (!edits) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
return edits.map((edit) => {
|
||||
return {
|
||||
start: compilerService.host.positionToLineCol(file, edit.span.start),
|
||||
end: compilerService.host.positionToLineCol(file, ts.textSpanEnd(edit.span)),
|
||||
newText: edit.newText ? edit.newText : ""
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getFormattingEditsAfterKeystroke(line: number, col: number, key: string, fileName: string): protocol.CodeEdit[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
var edits = compilerService.languageService.getFormattingEditsAfterKeystroke(file, position, key,
|
||||
compilerService.formatCodeOptions);
|
||||
if ((key == "\n") && ((!edits) || (edits.length == 0) || allEditsBeforePos(edits, position))) {
|
||||
// TODO: get these options from host
|
||||
var editorOptions: ts.EditorOptions = {
|
||||
IndentSize: 4,
|
||||
TabSize: 4,
|
||||
NewLineCharacter: "\n",
|
||||
ConvertTabsToSpaces: true,
|
||||
};
|
||||
var indentPosition = compilerService.languageService.getIndentationAtPosition(file, position, editorOptions);
|
||||
var spaces = generateSpaces(indentPosition);
|
||||
if (indentPosition > 0) {
|
||||
edits.push({ span: ts.createTextSpanFromBounds(position, position), newText: spaces });
|
||||
}
|
||||
}
|
||||
|
||||
if (!edits) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
return edits.map((edit) => {
|
||||
return {
|
||||
start: compilerService.host.positionToLineCol(file,
|
||||
edit.span.start),
|
||||
end: compilerService.host.positionToLineCol(file,
|
||||
ts.textSpanEnd(edit.span)),
|
||||
newText: edit.newText ? edit.newText : ""
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getCompletions(line: number, col: number, prefix: string, fileName: string): protocol.CompletionEntry[] {
|
||||
if (!prefix) {
|
||||
prefix = "";
|
||||
}
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
|
||||
var completions = compilerService.languageService.getCompletionsAtPosition(file, position);
|
||||
if (!completions) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => {
|
||||
if (completions.isMemberCompletion || entry.name.indexOf(prefix) == 0) {
|
||||
result.push(entry);
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
getCompletionEntryDetails(line: number, col: number,
|
||||
entryNames: string[], fileName: string): protocol.CompletionEntryDetails[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
|
||||
return entryNames.reduce((accum: protocol.CompletionEntryDetails[], entryName: string) => {
|
||||
var details = compilerService.languageService.getCompletionEntryDetails(file, position, entryName);
|
||||
if (details) {
|
||||
accum.push(details);
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
}
|
||||
|
||||
getDiagnostics(delay: number, fileNames: string[]) {
|
||||
var checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => {
|
||||
fileName = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(fileName);
|
||||
if (project) {
|
||||
accum.push({ fileName, project });
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
|
||||
if (checkList.length > 0) {
|
||||
this.updateErrorCheck(checkList, this.changeSeq,(n) => n == this.changeSeq, delay)
|
||||
}
|
||||
}
|
||||
|
||||
change(line: number, col: number, endLine: number, endCol: number, insertString: string, fileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (project) {
|
||||
var compilerService = project.compilerService;
|
||||
var start = compilerService.host.lineColToPosition(file, line, col);
|
||||
var end = compilerService.host.lineColToPosition(file, endLine, endCol);
|
||||
if (start >= 0) {
|
||||
compilerService.host.editScript(file, start, end, insertString);
|
||||
this.changeSeq++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reload(fileName: string, tempFileName: string, reqSeq = 0) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var tmpfile = ts.normalizePath(tempFileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (project) {
|
||||
this.changeSeq++;
|
||||
// make sure no changes happen before this one is finished
|
||||
project.compilerService.host.reloadScript(file, tmpfile,() => {
|
||||
this.output(undefined, CommandNames.Reload, reqSeq);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
saveToTmp(fileName: string, tempFileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var tmpfile = ts.normalizePath(tempFileName);
|
||||
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (project) {
|
||||
project.compilerService.host.saveTo(file, tmpfile);
|
||||
}
|
||||
}
|
||||
|
||||
closeClientFile(fileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
this.projectService.closeClientFile(file);
|
||||
}
|
||||
|
||||
decorateNavigationBarItem(project: Project, fileName: string, items: ts.NavigationBarItem[]): protocol.NavigationBarItem[] {
|
||||
if (!items) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
|
||||
return items.map(item => ({
|
||||
text: item.text,
|
||||
kind: item.kind,
|
||||
kindModifiers: item.kindModifiers,
|
||||
spans: item.spans.map(span => ({
|
||||
start: compilerService.host.positionToLineCol(fileName, span.start),
|
||||
end: compilerService.host.positionToLineCol(fileName, ts.textSpanEnd(span))
|
||||
})),
|
||||
childItems: this.decorateNavigationBarItem(project, fileName, item.childItems)
|
||||
}));
|
||||
}
|
||||
|
||||
getNavigationBarItems(fileName: string): protocol.NavigationBarItem[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var items = compilerService.languageService.getNavigationBarItems(file);
|
||||
if (!items) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
return this.decorateNavigationBarItem(project, fileName, items);
|
||||
}
|
||||
|
||||
getNavigateToItems(searchTerm: string, fileName: string): protocol.NavtoItem[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var navItems = sortNavItems(compilerService.languageService.getNavigateToItems(searchTerm));
|
||||
if (!navItems) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
return navItems.map((navItem) => {
|
||||
var start = compilerService.host.positionToLineCol(navItem.fileName, navItem.textSpan.start);
|
||||
var end = compilerService.host.positionToLineCol(navItem.fileName, ts.textSpanEnd(navItem.textSpan));
|
||||
var bakedItem: protocol.NavtoItem = {
|
||||
name: navItem.name,
|
||||
kind: navItem.kind,
|
||||
file: navItem.fileName,
|
||||
start: start,
|
||||
end: end,
|
||||
};
|
||||
if (navItem.kindModifiers && (navItem.kindModifiers != "")) {
|
||||
bakedItem.kindModifiers = navItem.kindModifiers;
|
||||
}
|
||||
if (navItem.matchKind != 'none') {
|
||||
bakedItem.matchKind = navItem.matchKind;
|
||||
}
|
||||
if (navItem.containerName && (navItem.containerName.length > 0)) {
|
||||
bakedItem.containerName = navItem.containerName;
|
||||
}
|
||||
if (navItem.containerKind && (navItem.containerKind.length > 0)) {
|
||||
bakedItem.containerKind = navItem.containerKind;
|
||||
}
|
||||
return bakedItem;
|
||||
});
|
||||
}
|
||||
|
||||
getBraceMatching(line: number, col: number, fileName: string): protocol.TextSpan[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
var compilerService = project.compilerService;
|
||||
var position = compilerService.host.lineColToPosition(file, line, col);
|
||||
|
||||
var spans = compilerService.languageService.getBraceMatchingAtPosition(file, position);
|
||||
if (!spans) {
|
||||
throw Errors.NoContent;
|
||||
}
|
||||
|
||||
return spans.map(span => ({
|
||||
start: compilerService.host.positionToLineCol(file, span.start),
|
||||
end: compilerService.host.positionToLineCol(file, span.start + span.length)
|
||||
}));
|
||||
}
|
||||
|
||||
onMessage(message: string) {
|
||||
try {
|
||||
var request = <protocol.Request>JSON.parse(message);
|
||||
var response: any;
|
||||
switch (request.command) {
|
||||
case CommandNames.Definition: {
|
||||
var defArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
response = this.getDefinition(defArgs.line, defArgs.col, defArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.References: {
|
||||
var refArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
response = this.getReferences(refArgs.line, refArgs.col, refArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Rename: {
|
||||
var renameArgs = <protocol.RenameRequestArgs>request.arguments;
|
||||
response = this.getRenameLocations(renameArgs.line, renameArgs.col, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Open: {
|
||||
var openArgs = <protocol.FileRequestArgs>request.arguments;
|
||||
this.openClientFile(openArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Quickinfo: {
|
||||
var quickinfoArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
response = this.getQuickInfo(quickinfoArgs.line, quickinfoArgs.col, quickinfoArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Format: {
|
||||
var formatArgs = <protocol.FormatRequestArgs>request.arguments;
|
||||
response = this.getFormattingEditsForRange(formatArgs.line, formatArgs.col, formatArgs.endLine, formatArgs.endCol, formatArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Formatonkey: {
|
||||
var formatOnKeyArgs = <protocol.FormatOnKeyRequestArgs>request.arguments;
|
||||
response = this.getFormattingEditsAfterKeystroke(formatOnKeyArgs.line, formatOnKeyArgs.col, formatOnKeyArgs.key, formatOnKeyArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Completions: {
|
||||
var completionsArgs = <protocol.CompletionsRequestArgs>request.arguments;
|
||||
response = this.getCompletions(request.arguments.line, request.arguments.col, completionsArgs.prefix, request.arguments.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.CompletionDetails: {
|
||||
var completionDetailsArgs = <protocol.CompletionDetailsRequestArgs>request.arguments;
|
||||
response = this.getCompletionEntryDetails(request.arguments.line, request.arguments.col, completionDetailsArgs.entryNames,
|
||||
request.arguments.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Geterr: {
|
||||
var geterrArgs = <protocol.GeterrRequestArgs>request.arguments;
|
||||
response = this.getDiagnostics(geterrArgs.delay, geterrArgs.files);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Change: {
|
||||
var changeArgs = <protocol.ChangeRequestArgs>request.arguments;
|
||||
this.change(changeArgs.line, changeArgs.col, changeArgs.endLine, changeArgs.endCol,
|
||||
changeArgs.insertString, changeArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Reload: {
|
||||
var reloadArgs = <protocol.ReloadRequestArgs>request.arguments;
|
||||
this.reload(reloadArgs.file, reloadArgs.tmpfile, request.seq);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Saveto: {
|
||||
var savetoArgs = <protocol.SavetoRequestArgs>request.arguments;
|
||||
this.saveToTmp(savetoArgs.file, savetoArgs.tmpfile);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Close: {
|
||||
var closeArgs = <protocol.FileRequestArgs>request.arguments;
|
||||
this.closeClientFile(closeArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Navto: {
|
||||
var navtoArgs = <protocol.NavtoRequestArgs>request.arguments;
|
||||
response = this.getNavigateToItems(navtoArgs.searchTerm, navtoArgs.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.Brace: {
|
||||
var braceArguments = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
response = this.getBraceMatching(braceArguments.line, braceArguments.col, braceArguments.file);
|
||||
break;
|
||||
}
|
||||
case CommandNames.NavBar: {
|
||||
var navBarArgs = <protocol.FileRequestArgs>request.arguments;
|
||||
response = this.getNavigationBarItems(navBarArgs.file);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.projectService.log("Unrecognized JSON command: " + message);
|
||||
this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (response) {
|
||||
this.output(response, request.command, request.seq);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
if (err instanceof OperationCanceledException) {
|
||||
// Handle cancellation exceptions
|
||||
}
|
||||
this.logError(err, message);
|
||||
this.output(undefined, request ? request.command : CommandNames.Unknown, request ? request.seq : 0, "Error processing request. " + err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,17 +14,17 @@ module ts.BreakpointResolver {
|
||||
}
|
||||
|
||||
var tokenAtLocation = getTokenAtPosition(sourceFile, position);
|
||||
var lineOfPosition = sourceFile.getLineAndCharacterFromPosition(position).line;
|
||||
if (sourceFile.getLineAndCharacterFromPosition(tokenAtLocation.getStart()).line > lineOfPosition) {
|
||||
var lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
|
||||
if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart()).line > lineOfPosition) {
|
||||
// Get previous token if the token is returned starts on new line
|
||||
// eg: var x =10; |--- curser is here
|
||||
// eg: var x =10; |--- cursor is here
|
||||
// var y = 10;
|
||||
// token at position will return var keyword on second line as the token but we would like to use
|
||||
// token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
|
||||
tokenAtLocation = findPrecedingToken(tokenAtLocation.pos, sourceFile);
|
||||
|
||||
// Its a blank line
|
||||
if (!tokenAtLocation || sourceFile.getLineAndCharacterFromPosition(tokenAtLocation.getEnd()).line !== lineOfPosition) {
|
||||
if (!tokenAtLocation || sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getEnd()).line !== lineOfPosition) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ module ts.BreakpointResolver {
|
||||
}
|
||||
|
||||
function spanInNodeIfStartsOnSameLine(node: Node, otherwiseOnNode?: Node): TextSpan {
|
||||
if (node && lineOfPosition === sourceFile.getLineAndCharacterFromPosition(node.getStart()).line) {
|
||||
if (node && lineOfPosition === sourceFile.getLineAndCharacterOfPosition(node.getStart()).line) {
|
||||
return spanInNode(node);
|
||||
}
|
||||
return spanInNode(otherwiseOnNode);
|
||||
@@ -69,7 +69,7 @@ module ts.BreakpointResolver {
|
||||
return textSpan(node);
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node.parent).operator === SyntaxKind.CommaToken) {
|
||||
if (node.parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node.parent).operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
// if this is comma expression, the breakpoint is possible in this expression
|
||||
return textSpan(node);
|
||||
}
|
||||
@@ -151,8 +151,9 @@ module ts.BreakpointResolver {
|
||||
return spanInForStatement(<ForStatement>node);
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
// span on for (a in ...)
|
||||
return textSpan(node, findNextToken((<ForInStatement>node).expression, node));
|
||||
return textSpan(node, findNextToken((<ForInStatement | ForOfStatement>node).expression, node));
|
||||
|
||||
case SyntaxKind.SwitchStatement:
|
||||
// span on switch(...)
|
||||
@@ -261,7 +262,8 @@ module ts.BreakpointResolver {
|
||||
|
||||
function spanInVariableDeclaration(variableDeclaration: VariableDeclaration): TextSpan {
|
||||
// If declaration of for in statement, just set the span in parent
|
||||
if (variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
|
||||
if (variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement ||
|
||||
variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
|
||||
return spanInNode(variableDeclaration.parent.parent);
|
||||
}
|
||||
|
||||
@@ -362,6 +364,7 @@ module ts.BreakpointResolver {
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return spanInNodeIfStartsOnSameLine(block.parent, block.statements[0]);
|
||||
|
||||
// Set span on previous token if it starts on same line otherwise on the first statement of the block
|
||||
|
||||
@@ -67,8 +67,8 @@ module ts.formatting {
|
||||
}
|
||||
|
||||
export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
|
||||
var line = sourceFile.getLineAndCharacterFromPosition(position).line;
|
||||
if (line === 1) {
|
||||
var line = sourceFile.getLineAndCharacterOfPosition(position).line;
|
||||
if (line === 0) {
|
||||
return [];
|
||||
}
|
||||
// get the span for the previous\current line
|
||||
@@ -100,7 +100,7 @@ module ts.formatting {
|
||||
export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
|
||||
// format from the beginning of the line
|
||||
var span = {
|
||||
pos: getStartLinePositionForPosition(start, sourceFile),
|
||||
pos: getLineStartPositionForPosition(start, sourceFile),
|
||||
end: end
|
||||
};
|
||||
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatSelection);
|
||||
@@ -112,7 +112,7 @@ module ts.formatting {
|
||||
return [];
|
||||
}
|
||||
var span = {
|
||||
pos: getStartLinePositionForPosition(parent.getStart(sourceFile), sourceFile),
|
||||
pos: getLineStartPositionForPosition(parent.getStart(sourceFile), sourceFile),
|
||||
end: parent.end
|
||||
};
|
||||
return formatSpan(span, sourceFile, options, rulesProvider, requestKind);
|
||||
@@ -283,7 +283,7 @@ module ts.formatting {
|
||||
var previousLine = Constants.Unknown;
|
||||
var childKind = SyntaxKind.Unknown;
|
||||
while (n) {
|
||||
var line = sourceFile.getLineAndCharacterFromPosition(n.getStart(sourceFile)).line;
|
||||
var line = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)).line;
|
||||
if (previousLine !== Constants.Unknown && line !== previousLine) {
|
||||
break;
|
||||
}
|
||||
@@ -327,7 +327,7 @@ module ts.formatting {
|
||||
formattingScanner.advance();
|
||||
|
||||
if (formattingScanner.isOnToken()) {
|
||||
var startLine = sourceFile.getLineAndCharacterFromPosition(enclosingNode.getStart(sourceFile)).line;
|
||||
var startLine = sourceFile.getLineAndCharacterOfPosition(enclosingNode.getStart(sourceFile)).line;
|
||||
var delta = getOwnOrInheritedDelta(enclosingNode, options, sourceFile);
|
||||
processNode(enclosingNode, enclosingNode, startLine, initialIndentation, delta);
|
||||
}
|
||||
@@ -357,8 +357,8 @@ module ts.formatting {
|
||||
}
|
||||
}
|
||||
else {
|
||||
var startLine = sourceFile.getLineAndCharacterFromPosition(startPos).line;
|
||||
var startLinePosition = getStartLinePositionForPosition(startPos, sourceFile);
|
||||
var startLine = sourceFile.getLineAndCharacterOfPosition(startPos).line;
|
||||
var startLinePosition = getLineStartPositionForPosition(startPos, sourceFile);
|
||||
var column = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, startPos, sourceFile, options);
|
||||
if (startLine !== parentStartLine || startPos === column) {
|
||||
return column
|
||||
@@ -521,7 +521,7 @@ module ts.formatting {
|
||||
|
||||
var childStartPos = child.getStart(sourceFile);
|
||||
|
||||
var childStart = sourceFile.getLineAndCharacterFromPosition(childStartPos);
|
||||
var childStart = sourceFile.getLineAndCharacterOfPosition(childStartPos);
|
||||
|
||||
// if child is a list item - try to get its indentation
|
||||
var childIndentationAmount = Constants.Unknown;
|
||||
@@ -594,7 +594,7 @@ module ts.formatting {
|
||||
}
|
||||
else if (tokenInfo.token.kind === listStartToken) {
|
||||
// consume list start token
|
||||
startLine = sourceFile.getLineAndCharacterFromPosition(tokenInfo.token.pos).line;
|
||||
startLine = sourceFile.getLineAndCharacterOfPosition(tokenInfo.token.pos).line;
|
||||
var indentation =
|
||||
computeIndentation(tokenInfo.token, startLine, Constants.Unknown, parent, parentDynamicIndentation, startLine);
|
||||
|
||||
@@ -641,7 +641,7 @@ module ts.formatting {
|
||||
var lineAdded: boolean;
|
||||
var isTokenInRange = rangeContainsRange(originalRange, currentTokenInfo.token);
|
||||
|
||||
var tokenStart = sourceFile.getLineAndCharacterFromPosition(currentTokenInfo.token.pos);
|
||||
var tokenStart = sourceFile.getLineAndCharacterOfPosition(currentTokenInfo.token.pos);
|
||||
if (isTokenInRange) {
|
||||
var rangeHasError = rangeContainsError(currentTokenInfo.token);
|
||||
// save prevStartLine since processRange will overwrite this value with current ones
|
||||
@@ -674,7 +674,7 @@ module ts.formatting {
|
||||
continue;
|
||||
}
|
||||
|
||||
var triviaStartLine = sourceFile.getLineAndCharacterFromPosition(triviaItem.pos).line;
|
||||
var triviaStartLine = sourceFile.getLineAndCharacterOfPosition(triviaItem.pos).line;
|
||||
switch (triviaItem.kind) {
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
var commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind);
|
||||
@@ -712,7 +712,7 @@ module ts.formatting {
|
||||
for (var i = 0, len = trivia.length; i < len; ++i) {
|
||||
var triviaItem = trivia[i];
|
||||
if (isComment(triviaItem.kind) && rangeContainsRange(originalRange, triviaItem)) {
|
||||
var triviaItemStart = sourceFile.getLineAndCharacterFromPosition(triviaItem.pos);
|
||||
var triviaItemStart = sourceFile.getLineAndCharacterOfPosition(triviaItem.pos);
|
||||
processRange(triviaItem, triviaItemStart, parent, contextNode, dynamicIndentation);
|
||||
}
|
||||
}
|
||||
@@ -729,7 +729,7 @@ module ts.formatting {
|
||||
if (!rangeHasError && !previousRangeHasError) {
|
||||
if (!previousRange) {
|
||||
// trim whitespaces starting from the beginning of the span up to the current line
|
||||
var originalStart = sourceFile.getLineAndCharacterFromPosition(originalRange.pos);
|
||||
var originalStart = sourceFile.getLineAndCharacterOfPosition(originalRange.pos);
|
||||
trimTrailingWhitespacesForLines(originalStart.line, rangeStart.line);
|
||||
}
|
||||
else {
|
||||
@@ -807,18 +807,18 @@ module ts.formatting {
|
||||
recordReplace(pos, 0, indentationString);
|
||||
}
|
||||
else {
|
||||
var tokenStart = sourceFile.getLineAndCharacterFromPosition(pos);
|
||||
if (indentation !== tokenStart.character - 1) {
|
||||
var tokenStart = sourceFile.getLineAndCharacterOfPosition(pos);
|
||||
if (indentation !== tokenStart.character) {
|
||||
var startLinePosition = getStartPositionOfLine(tokenStart.line, sourceFile);
|
||||
recordReplace(startLinePosition, tokenStart.character - 1, indentationString);
|
||||
recordReplace(startLinePosition, tokenStart.character, indentationString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function indentMultilineComment(commentRange: TextRange, indentation: number, firstLineIsIndented: boolean) {
|
||||
// split comment in lines
|
||||
var startLine = sourceFile.getLineAndCharacterFromPosition(commentRange.pos).line;
|
||||
var endLine = sourceFile.getLineAndCharacterFromPosition(commentRange.end).line;
|
||||
var startLine = sourceFile.getLineAndCharacterOfPosition(commentRange.pos).line;
|
||||
var endLine = sourceFile.getLineAndCharacterOfPosition(commentRange.end).line;
|
||||
|
||||
if (startLine === endLine) {
|
||||
if (!firstLineIsIndented) {
|
||||
|
||||
@@ -71,8 +71,8 @@ module ts.formatting {
|
||||
|
||||
public TokensAreOnSameLine(): boolean {
|
||||
if (this.tokensAreOnSameLine === undefined) {
|
||||
var startLine = this.sourceFile.getLineAndCharacterFromPosition(this.currentTokenSpan.pos).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterFromPosition(this.nextTokenSpan.pos).line;
|
||||
var startLine = this.sourceFile.getLineAndCharacterOfPosition(this.currentTokenSpan.pos).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterOfPosition(this.nextTokenSpan.pos).line;
|
||||
this.tokensAreOnSameLine = (startLine == endLine);
|
||||
}
|
||||
|
||||
@@ -96,8 +96,8 @@ module ts.formatting {
|
||||
}
|
||||
|
||||
private NodeIsOnOneLine(node: Node): boolean {
|
||||
var startLine = this.sourceFile.getLineAndCharacterFromPosition(node.getStart(this.sourceFile)).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterFromPosition(node.getEnd()).line;
|
||||
var startLine = this.sourceFile.getLineAndCharacterOfPosition(node.getStart(this.sourceFile)).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line;
|
||||
return startLine == endLine;
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ module ts.formatting {
|
||||
var openBrace = findChildOfKind(node, SyntaxKind.OpenBraceToken, this.sourceFile);
|
||||
var closeBrace = findChildOfKind(node, SyntaxKind.CloseBraceToken, this.sourceFile);
|
||||
if (openBrace && closeBrace) {
|
||||
var startLine = this.sourceFile.getLineAndCharacterFromPosition(openBrace.getEnd()).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterFromPosition(closeBrace.getStart(this.sourceFile)).line;
|
||||
var startLine = this.sourceFile.getLineAndCharacterOfPosition(openBrace.getEnd()).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterOfPosition(closeBrace.getStart(this.sourceFile)).line;
|
||||
return startLine === endLine;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -93,17 +93,16 @@ module ts.formatting {
|
||||
savedPos = scanner.getStartPos();
|
||||
}
|
||||
|
||||
function shouldRescanGreaterThanToken(container: Node): boolean {
|
||||
if (container.kind !== SyntaxKind.BinaryExpression) {
|
||||
return false;
|
||||
}
|
||||
switch ((<BinaryExpression>container).operator) {
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
return true;
|
||||
function shouldRescanGreaterThanToken(node: Node): boolean {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -164,7 +163,7 @@ module ts.formatting {
|
||||
|
||||
if (expectedScanAction === ScanAction.RescanGreaterThanToken && currentToken === SyntaxKind.GreaterThanToken) {
|
||||
currentToken = scanner.reScanGreaterToken();
|
||||
Debug.assert((<BinaryExpression>n).operator === currentToken);
|
||||
Debug.assert(n.kind === currentToken);
|
||||
lastScanAction = ScanAction.RescanGreaterThanToken;
|
||||
}
|
||||
else if (expectedScanAction === ScanAction.RescanSlashToken && startsWithSlashToken(currentToken)) {
|
||||
|
||||
@@ -464,6 +464,9 @@ module ts.formatting {
|
||||
// "in" keyword in for (var x in []) { }
|
||||
case SyntaxKind.ForInStatement:
|
||||
return context.currentTokenSpan.kind === SyntaxKind.InKeyword || context.nextTokenSpan.kind === SyntaxKind.InKeyword;
|
||||
// Technically, "of" is not a binary operator, but format it the same way as "in"
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return context.currentTokenSpan.kind === SyntaxKind.OfKeyword || context.nextTokenSpan.kind === SyntaxKind.OfKeyword;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -592,6 +595,7 @@ module ts.formatting {
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.TryStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
|
||||
@@ -24,7 +24,7 @@ module ts.formatting {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var lineAtPosition = sourceFile.getLineAndCharacterFromPosition(position).line;
|
||||
var lineAtPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
|
||||
|
||||
if (precedingToken.kind === SyntaxKind.CommaToken && precedingToken.parent.kind !== SyntaxKind.BinaryExpression) {
|
||||
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
|
||||
@@ -74,7 +74,7 @@ module ts.formatting {
|
||||
}
|
||||
|
||||
export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: FormatCodeOptions): number {
|
||||
var start = sourceFile.getLineAndCharacterFromPosition(n.getStart(sourceFile));
|
||||
var start = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
|
||||
return getIndentationForNodeWorker(n, start, ignoreActualIndentationRange, /*indentationDelta*/ 0, sourceFile, options);
|
||||
}
|
||||
|
||||
@@ -135,10 +135,10 @@ module ts.formatting {
|
||||
function getParentStart(parent: Node, child: Node, sourceFile: SourceFile): LineAndCharacter {
|
||||
var containingList = getContainingList(child, sourceFile);
|
||||
if (containingList) {
|
||||
return sourceFile.getLineAndCharacterFromPosition(containingList.pos);
|
||||
return sourceFile.getLineAndCharacterOfPosition(containingList.pos);
|
||||
}
|
||||
|
||||
return sourceFile.getLineAndCharacterFromPosition(parent.getStart(sourceFile));
|
||||
return sourceFile.getLineAndCharacterOfPosition(parent.getStart(sourceFile));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -204,7 +204,7 @@ module ts.formatting {
|
||||
}
|
||||
|
||||
function getStartLineAndCharacterForNode(n: Node, sourceFile: SourceFile): LineAndCharacter {
|
||||
return sourceFile.getLineAndCharacterFromPosition(n.getStart(sourceFile));
|
||||
return sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
|
||||
}
|
||||
|
||||
function positionBelongsToNode(candidate: Node, position: number, sourceFile: SourceFile): boolean {
|
||||
@@ -279,7 +279,6 @@ module ts.formatting {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
Debug.assert(index >= 0 && index < list.length);
|
||||
var node = list[index];
|
||||
@@ -292,7 +291,7 @@ module ts.formatting {
|
||||
continue;
|
||||
}
|
||||
// skip list items that ends on the same line with the current list element
|
||||
var prevEndLine = sourceFile.getLineAndCharacterFromPosition(list[i].end).line;
|
||||
var prevEndLine = sourceFile.getLineAndCharacterOfPosition(list[i].end).line;
|
||||
if (prevEndLine !== lineAndCharacter.line) {
|
||||
return findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter, sourceFile, options);
|
||||
}
|
||||
@@ -303,7 +302,7 @@ module ts.formatting {
|
||||
}
|
||||
|
||||
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
var lineStart = sourceFile.getPositionFromLineAndCharacter(lineAndCharacter.line, 1);
|
||||
var lineStart = sourceFile.getPositionOfLineAndCharacter(lineAndCharacter.line, 0);
|
||||
return findFirstNonWhitespaceColumn(lineStart, lineStart + lineAndCharacter.character, sourceFile, options);
|
||||
}
|
||||
|
||||
@@ -359,6 +358,7 @@ module ts.formatting {
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
|
||||
@@ -126,7 +126,7 @@ module ts.formatting {
|
||||
static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia]));
|
||||
static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
|
||||
static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
|
||||
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword]);
|
||||
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword]);
|
||||
static UnaryPrefixOperators = TokenRange.FromTokens([SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
|
||||
static UnaryPrefixExpressions = TokenRange.FromTokens([SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPreincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
@@ -134,7 +134,7 @@ module ts.formatting {
|
||||
static UnaryPredecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPostdecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
static Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]);
|
||||
static TypeNames = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
|
||||
static TypeNames = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword, SyntaxKind.SymbolKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
module ts.NavigateTo {
|
||||
type RawNavigateToItem = { name: string; fileName: string; matchKind: MatchKind; declaration: Declaration };
|
||||
|
||||
enum MatchKind {
|
||||
none = 0,
|
||||
exact = 1,
|
||||
substring = 2,
|
||||
prefix = 3
|
||||
}
|
||||
|
||||
export function getNavigateToItems(program: Program, cancellationToken: CancellationTokenObject, searchValue: string, maxResultCount: number): NavigateToItem[]{
|
||||
// Split search value in terms array
|
||||
var terms = searchValue.split(" ");
|
||||
|
||||
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
|
||||
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
|
||||
|
||||
var rawItems: RawNavigateToItem[] = [];
|
||||
|
||||
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
var fileName = sourceFile.fileName;
|
||||
var declarations = sourceFile.getNamedDeclarations();
|
||||
for (var i = 0, n = declarations.length; i < n; i++) {
|
||||
var declaration = declarations[i];
|
||||
// TODO(jfreeman): Skip this declaration if it has a computed name
|
||||
var name = (<Identifier>declaration.name).text;
|
||||
var matchKind = getMatchKind(searchTerms, name);
|
||||
if (matchKind !== MatchKind.none) {
|
||||
rawItems.push({ name, fileName, matchKind, declaration });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rawItems.sort(compareNavigateToItems);
|
||||
if (maxResultCount !== undefined) {
|
||||
rawItems = rawItems.slice(0, maxResultCount);
|
||||
}
|
||||
|
||||
var items = map(rawItems, createNavigateToItem);
|
||||
|
||||
return items;
|
||||
|
||||
// This means "compare in a case insensitive manner."
|
||||
var baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
|
||||
function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem) {
|
||||
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
|
||||
// Right now we just sort by kind first, and then by name of the item.
|
||||
// We first sort case insensitively. So "Aaa" will come before "bar".
|
||||
// Then we sort case sensitively, so "aaa" will come before "Aaa".
|
||||
return i1.matchKind - i2.matchKind ||
|
||||
i1.name.localeCompare(i2.name, undefined, baseSensitivity) ||
|
||||
i1.name.localeCompare(i2.name);
|
||||
}
|
||||
|
||||
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
|
||||
var declaration = rawItem.declaration;
|
||||
var container = <Declaration>getContainerNode(declaration);
|
||||
return {
|
||||
name: rawItem.name,
|
||||
kind: getNodeKind(declaration),
|
||||
kindModifiers: getNodeModifiers(declaration),
|
||||
matchKind: MatchKind[rawItem.matchKind],
|
||||
fileName: rawItem.fileName,
|
||||
textSpan: createTextSpanFromBounds(declaration.getStart(), declaration.getEnd()),
|
||||
// TODO(jfreeman): What should be the containerName when the container has a computed name?
|
||||
containerName: container && container.name ? (<Identifier>container.name).text : "",
|
||||
containerKind: container && container.name ? getNodeKind(container) : ""
|
||||
};
|
||||
}
|
||||
|
||||
function hasAnyUpperCaseCharacter(s: string): boolean {
|
||||
for (var i = 0, n = s.length; i < n; i++) {
|
||||
var c = s.charCodeAt(i);
|
||||
if ((CharacterCodes.A <= c && c <= CharacterCodes.Z) ||
|
||||
(c >= CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
|
||||
var matchKind = MatchKind.none;
|
||||
|
||||
if (name) {
|
||||
for (var j = 0, n = searchTerms.length; j < n; j++) {
|
||||
var searchTerm = searchTerms[j];
|
||||
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
|
||||
// in case of case-insensitive search searchTerm.term will already be lower-cased
|
||||
var index = nameToSearch.indexOf(searchTerm.term);
|
||||
if (index < 0) {
|
||||
// Didn't match.
|
||||
return MatchKind.none;
|
||||
}
|
||||
|
||||
var termKind = MatchKind.substring;
|
||||
if (index === 0) {
|
||||
// here we know that match occur at the beginning of the string.
|
||||
// if search term and declName has the same length - we have an exact match, otherwise declName have longer length and this will be prefix match
|
||||
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
|
||||
}
|
||||
|
||||
// Update our match kind if we don't have one, or if this match is better.
|
||||
if (matchKind === MatchKind.none || termKind < matchKind) {
|
||||
matchKind = termKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,8 +97,7 @@ module ts.NavigationBar {
|
||||
function sortNodes(nodes: Node[]): Node[] {
|
||||
return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => {
|
||||
if (n1.name && n2.name) {
|
||||
// TODO(jfreeman): How do we sort declarations with computed names?
|
||||
return (<Identifier>n1.name).text.localeCompare((<Identifier>n2.name).text);
|
||||
return getPropertyNameForPropertyNameNode(n1.name).localeCompare(getPropertyNameForPropertyNameNode(n2.name));
|
||||
}
|
||||
else if (n1.name) {
|
||||
return 1;
|
||||
@@ -426,7 +425,7 @@ module ts.NavigationBar {
|
||||
// Add the constructor parameters in as children of the class (for property parameters).
|
||||
// Note that *all* parameters will be added to the nodes array, but parameters that
|
||||
// are not properties will be filtered out later by createChildItem.
|
||||
var nodes: Node[] = removeComputedProperties(node);
|
||||
var nodes: Node[] = removeDynamicallyNamedProperties(node);
|
||||
if (constructor) {
|
||||
nodes.push.apply(nodes, constructor.parameters);
|
||||
}
|
||||
@@ -455,7 +454,7 @@ module ts.NavigationBar {
|
||||
}
|
||||
|
||||
function createIterfaceItem(node: InterfaceDeclaration): ts.NavigationBarItem {
|
||||
var childItems = getItemsWorker(sortNodes(removeComputedProperties(node)), createChildItem);
|
||||
var childItems = getItemsWorker(sortNodes(removeDynamicallyNamedProperties(node)), createChildItem);
|
||||
return getNavigationBarItem(
|
||||
node.name.text,
|
||||
ts.ScriptElementKind.interfaceElement,
|
||||
@@ -466,10 +465,17 @@ module ts.NavigationBar {
|
||||
}
|
||||
}
|
||||
|
||||
function removeComputedProperties(node: ClassDeclaration | InterfaceDeclaration | EnumDeclaration): Declaration[] {
|
||||
function removeComputedProperties(node: EnumDeclaration): Declaration[] {
|
||||
return filter<Declaration>(node.members, member => member.name === undefined || member.name.kind !== SyntaxKind.ComputedPropertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like removeComputedProperties, but retains the properties with well known symbol names
|
||||
*/
|
||||
function removeDynamicallyNamedProperties(node: ClassDeclaration | InterfaceDeclaration): Declaration[]{
|
||||
return filter<Declaration>(node.members, member => !hasDynamicName(member));
|
||||
}
|
||||
|
||||
function getInnermostModule(node: ModuleDeclaration): ModuleDeclaration {
|
||||
while (node.body.kind === SyntaxKind.ModuleDeclaration) {
|
||||
node = <ModuleDeclaration>node.body;
|
||||
|
||||
@@ -61,6 +61,7 @@ module ts {
|
||||
// to be the entire span of the parent.
|
||||
if (parent.kind === SyntaxKind.DoStatement ||
|
||||
parent.kind === SyntaxKind.ForInStatement ||
|
||||
parent.kind === SyntaxKind.ForOfStatement ||
|
||||
parent.kind === SyntaxKind.ForStatement ||
|
||||
parent.kind === SyntaxKind.IfStatement ||
|
||||
parent.kind === SyntaxKind.WhileStatement ||
|
||||
|
||||
@@ -0,0 +1,813 @@
|
||||
module ts {
|
||||
// Note(cyrusn): this enum is ordered from strongest match type to weakest match type.
|
||||
export enum PatternMatchKind {
|
||||
Exact,
|
||||
Prefix,
|
||||
Substring,
|
||||
CamelCase
|
||||
}
|
||||
|
||||
// Information about a match made by the pattern matcher between a candidate and the
|
||||
// search pattern.
|
||||
export interface PatternMatch {
|
||||
// What kind of match this was. Exact matches are better than prefix matches which are
|
||||
// better than substring matches which are better than CamelCase matches.
|
||||
kind: PatternMatchKind;
|
||||
|
||||
// If this was a camel case match, how strong the match is. Higher number means
|
||||
// it was a better match.
|
||||
camelCaseWeight?: number;
|
||||
|
||||
// If this was a match where all constituent parts of the candidate and search pattern
|
||||
// matched case sensitively or case insensitively. Case sensitive matches of the kind
|
||||
// are better matches than insensitive matches.
|
||||
isCaseSensitive: boolean;
|
||||
|
||||
// Whether or not this match occurred with the punctuation from the search pattern stripped
|
||||
// out or not. Matches without the punctuation stripped are better than ones with punctuation
|
||||
// stripped.
|
||||
punctuationStripped: boolean;
|
||||
}
|
||||
|
||||
// The pattern matcher maintains an internal cache of information as it is used. Therefore,
|
||||
// you should not keep it around forever and should get and release the matcher appropriately
|
||||
// once you no longer need it.
|
||||
export interface PatternMatcher {
|
||||
// Used to match a candidate against the last segment of a possibly dotted pattern. This
|
||||
// is useful as a quick check to prevent having to compute a container before calling
|
||||
// "getMatches".
|
||||
//
|
||||
// For example, if the search pattern is "ts.c.SK" and the candidate is "SyntaxKind", then
|
||||
// this will return a successful match, having only tested "SK" against "SyntaxKind". At
|
||||
// that point a call can be made to 'getMatches("SyntaxKind", "ts.compiler")', with the
|
||||
// work to create 'ts.compiler' only being done once the first match succeeded.
|
||||
getMatchesForLastSegmentOfPattern(candidate: string): PatternMatch[];
|
||||
|
||||
// Fully checks a candidate, with an dotted container, against the search pattern.
|
||||
// The candidate must match the last part of the search pattern, and the dotted container
|
||||
// must match the preceding segments of the pattern.
|
||||
getMatches(candidate: string, dottedContainer: string): PatternMatch[];
|
||||
|
||||
// Whether or not the pattern contained dots or not. Clients can use this to determine
|
||||
// If they should call getMatches, or if getMatchesForLastSegmentOfPattern is sufficient.
|
||||
patternContainsDots: boolean;
|
||||
}
|
||||
|
||||
// First we break up the pattern given by dots. Each portion of the pattern between the
|
||||
// dots is a 'Segment'. The 'Segment' contains information about the entire section of
|
||||
// text between the dots, as well as information about any individual 'Words' that we
|
||||
// can break the segment into. A 'Word' is simply a contiguous sequence of characters
|
||||
// that can appear in a typescript identifier. So "GetKeyword" would be one word, while
|
||||
// "Get Keyword" would be two words. Once we have the individual 'words', we break those
|
||||
// into constituent 'character spans' of interest. For example, while 'UIElement' is one
|
||||
// word, it make character spans corresponding to "U", "I" and "Element". These spans
|
||||
// are then used when doing camel cased matches against candidate patterns.
|
||||
interface Segment {
|
||||
// Information about the entire piece of text between the dots. For example, if the
|
||||
// text between the dots is 'GetKeyword', then TotalTextChunk.Text will be 'GetKeyword' and
|
||||
// TotalTextChunk.CharacterSpans will correspond to 'Get', 'Keyword'.
|
||||
totalTextChunk: TextChunk;
|
||||
|
||||
// Information about the subwords compromising the total word. For example, if the
|
||||
// text between the dots is 'GetFoo KeywordBar', then the subwords will be 'GetFoo'
|
||||
// and 'KeywordBar'. Those individual words will have CharacterSpans of ('Get' and
|
||||
// 'Foo') and('Keyword' and 'Bar') respectively.
|
||||
subWordTextChunks: TextChunk[];
|
||||
}
|
||||
|
||||
// Information about a chunk of text from the pattern. The chunk is a piece of text, with
|
||||
// cached information about the character spans within in. Character spans are used for
|
||||
// camel case matching.
|
||||
interface TextChunk {
|
||||
// The text of the chunk. This should be a contiguous sequence of character that could
|
||||
// occur in a symbol name.
|
||||
text: string;
|
||||
|
||||
// The text of a chunk in lower case. Cached because it is needed often to check for
|
||||
// case insensitive matches.
|
||||
textLowerCase: string;
|
||||
|
||||
// Whether or not this chunk is entirely lowercase. We have different rules when searching
|
||||
// for something entirely lowercase or not.
|
||||
isLowerCase: boolean;
|
||||
|
||||
// The spans in this text chunk that we think are of interest and should be matched
|
||||
// independently. For example, if the chunk is for "UIElement" the the spans of interest
|
||||
// correspond to "U", "I" and "Element". If "UIElement" isn't found as an exaxt, prefix.
|
||||
// or substring match, then the character spans will be used to attempt a camel case match.
|
||||
characterSpans: TextSpan[];
|
||||
}
|
||||
|
||||
function createPatternMatch(kind: PatternMatchKind, punctuationStripped: boolean, isCaseSensitive: boolean, camelCaseWeight?: number): PatternMatch {
|
||||
return {
|
||||
kind,
|
||||
punctuationStripped,
|
||||
isCaseSensitive,
|
||||
camelCaseWeight
|
||||
};
|
||||
}
|
||||
|
||||
export function createPatternMatcher(pattern: string): PatternMatcher {
|
||||
// We'll often see the same candidate string many times when searching (For example, when
|
||||
// we see the name of a module that is used everywhere, or the name of an overload). As
|
||||
// such, we cache the information we compute about the candidate for the life of this
|
||||
// pattern matcher so we don't have to compute it multiple times.
|
||||
var stringToWordSpans: Map<TextSpan[]> = {};
|
||||
|
||||
pattern = pattern.trim();
|
||||
|
||||
var fullPatternSegment = createSegment(pattern);
|
||||
var dotSeparatedSegments = pattern.split(".").map(p => createSegment(p.trim()));
|
||||
var invalidPattern = dotSeparatedSegments.length === 0 || forEach(dotSeparatedSegments, segmentIsInvalid);
|
||||
|
||||
return {
|
||||
getMatches,
|
||||
getMatchesForLastSegmentOfPattern,
|
||||
patternContainsDots: dotSeparatedSegments.length > 1
|
||||
};
|
||||
|
||||
// Quick checks so we can bail out when asked to match a candidate.
|
||||
function skipMatch(candidate: string) {
|
||||
return invalidPattern || !candidate;
|
||||
}
|
||||
|
||||
function getMatchesForLastSegmentOfPattern(candidate: string): PatternMatch[] {
|
||||
if (skipMatch(candidate)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return matchSegment(candidate, lastOrUndefined(dotSeparatedSegments));
|
||||
}
|
||||
|
||||
function getMatches(candidate: string, dottedContainer: string): PatternMatch[] {
|
||||
if (skipMatch(candidate)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// First, check that the last part of the dot separated pattern matches the name of the
|
||||
// candidate. If not, then there's no point in proceeding and doing the more
|
||||
// expensive work.
|
||||
var candidateMatch = matchSegment(candidate, lastOrUndefined(dotSeparatedSegments));
|
||||
if (!candidateMatch) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
dottedContainer = dottedContainer || "";
|
||||
var containerParts = dottedContainer.split(".");
|
||||
|
||||
// -1 because the last part was checked against the name, and only the rest
|
||||
// of the parts are checked against the container.
|
||||
if (dotSeparatedSegments.length - 1 > containerParts.length) {
|
||||
// There weren't enough container parts to match against the pattern parts.
|
||||
// So this definitely doesn't match.
|
||||
return null;
|
||||
}
|
||||
|
||||
// So far so good. Now break up the container for the candidate and check if all
|
||||
// the dotted parts match up correctly.
|
||||
var totalMatch = candidateMatch;
|
||||
|
||||
for (var i = dotSeparatedSegments.length - 2, j = containerParts.length - 1;
|
||||
i >= 0;
|
||||
i--, j--) {
|
||||
|
||||
var segment = dotSeparatedSegments[i];
|
||||
var containerName = containerParts[j];
|
||||
|
||||
var containerMatch = matchSegment(containerName, segment);
|
||||
if (!containerMatch) {
|
||||
// This container didn't match the pattern piece. So there's no match at all.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addRange(totalMatch, containerMatch);
|
||||
}
|
||||
|
||||
// Success, this symbol's full name matched against the dotted name the user was asking
|
||||
// about.
|
||||
return totalMatch;
|
||||
}
|
||||
|
||||
function getWordSpans(word: string): TextSpan[] {
|
||||
if (!hasProperty(stringToWordSpans, word)) {
|
||||
stringToWordSpans[word] = breakIntoWordSpans(word);
|
||||
}
|
||||
|
||||
return stringToWordSpans[word];
|
||||
}
|
||||
|
||||
function matchTextChunk(candidate: string, chunk: TextChunk, punctuationStripped: boolean): PatternMatch {
|
||||
var index = indexOfIgnoringCase(candidate, chunk.textLowerCase);
|
||||
if (index === 0) {
|
||||
if (chunk.text.length === candidate.length) {
|
||||
// a) Check if the part matches the candidate entirely, in an case insensitive or
|
||||
// sensitive manner. If it does, return that there was an exact match.
|
||||
return createPatternMatch(PatternMatchKind.Exact, punctuationStripped, /*isCaseSensitive:*/ candidate === chunk.text);
|
||||
}
|
||||
else {
|
||||
// b) Check if the part is a prefix of the candidate, in a case insensitive or sensitive
|
||||
// manner. If it does, return that there was a prefix match.
|
||||
return createPatternMatch(PatternMatchKind.Prefix, punctuationStripped, /*isCaseSensitive:*/ startsWith(candidate, chunk.text));
|
||||
}
|
||||
}
|
||||
|
||||
var isLowercase = chunk.isLowerCase;
|
||||
if (isLowercase) {
|
||||
if (index > 0) {
|
||||
// c) If the part is entirely lowercase, then check if it is contained anywhere in the
|
||||
// candidate in a case insensitive manner. If so, return that there was a substring
|
||||
// match.
|
||||
//
|
||||
// Note: We only have a substring match if the lowercase part is prefix match of some
|
||||
// word part. That way we don't match something like 'Class' when the user types 'a'.
|
||||
// But we would match 'FooAttribute' (since 'Attribute' starts with 'a').
|
||||
var wordSpans = getWordSpans(candidate);
|
||||
for (var i = 0, n = wordSpans.length; i < n; i++) {
|
||||
var span = wordSpans[i]
|
||||
if (partStartsWith(candidate, span, chunk.text, /*ignoreCase:*/ true)) {
|
||||
return createPatternMatch(PatternMatchKind.Substring, punctuationStripped,
|
||||
/*isCaseSensitive:*/ partStartsWith(candidate, span, chunk.text, /*ignoreCase:*/ false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// d) If the part was not entirely lowercase, then check if it is contained in the
|
||||
// candidate in a case *sensitive* manner. If so, return that there was a substring
|
||||
// match.
|
||||
if (candidate.indexOf(chunk.text) > 0) {
|
||||
return createPatternMatch(PatternMatchKind.Substring, punctuationStripped, /*isCaseSensitive:*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLowercase) {
|
||||
// e) If the part was not entirely lowercase, then attempt a camel cased match as well.
|
||||
if (chunk.characterSpans.length > 0) {
|
||||
var candidateParts = getWordSpans(candidate);
|
||||
var camelCaseWeight = tryCamelCaseMatch(candidate, candidateParts, chunk, /*ignoreCase:*/ false);
|
||||
if (camelCaseWeight !== undefined) {
|
||||
return createPatternMatch(PatternMatchKind.CamelCase, punctuationStripped, /*isCaseSensitive:*/ true, /*camelCaseWeight:*/ camelCaseWeight);
|
||||
}
|
||||
|
||||
camelCaseWeight = tryCamelCaseMatch(candidate, candidateParts, chunk, /*ignoreCase:*/ true);
|
||||
if (camelCaseWeight !== undefined) {
|
||||
return createPatternMatch(PatternMatchKind.CamelCase, punctuationStripped, /*isCaseSensitive:*/ false, /*camelCaseWeight:*/ camelCaseWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isLowercase) {
|
||||
// f) Is the pattern a substring of the candidate starting on one of the candidate's word boundaries?
|
||||
|
||||
// We could check every character boundary start of the candidate for the pattern. However, that's
|
||||
// an m * n operation in the wost case. Instead, find the first instance of the pattern
|
||||
// substring, and see if it starts on a capital letter. It seems unlikely that the user will try to
|
||||
// filter the list based on a substring that starts on a capital letter and also with a lowercase one.
|
||||
// (Pattern: fogbar, Candidate: quuxfogbarFogBar).
|
||||
if (chunk.text.length < candidate.length) {
|
||||
if (index > 0 && isUpperCaseLetter(candidate.charCodeAt(index))) {
|
||||
return createPatternMatch(PatternMatchKind.Substring, punctuationStripped, /*isCaseSensitive:*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function containsSpaceOrAsterisk(text: string): boolean {
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
var ch = text.charCodeAt(i);
|
||||
if (ch === CharacterCodes.space || ch === CharacterCodes.asterisk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function matchSegment(candidate: string, segment: Segment): PatternMatch[] {
|
||||
// First check if the segment matches as is. This is also useful if the segment contains
|
||||
// characters we would normally strip when splitting into parts that we also may want to
|
||||
// match in the candidate. For example if the segment is "@int" and the candidate is
|
||||
// "@int", then that will show up as an exact match here.
|
||||
//
|
||||
// Note: if the segment contains a space or an asterisk then we must assume that it's a
|
||||
// multi-word segment.
|
||||
if (!containsSpaceOrAsterisk(segment.totalTextChunk.text)) {
|
||||
var match = matchTextChunk(candidate, segment.totalTextChunk, /*punctuationStripped:*/ false);
|
||||
if (match) {
|
||||
return [match];
|
||||
}
|
||||
}
|
||||
|
||||
// The logic for pattern matching is now as follows:
|
||||
//
|
||||
// 1) Break the segment passed in into words. Breaking is rather simple and a
|
||||
// good way to think about it that if gives you all the individual alphanumeric words
|
||||
// of the pattern.
|
||||
//
|
||||
// 2) For each word try to match the word against the candidate value.
|
||||
//
|
||||
// 3) Matching is as follows:
|
||||
//
|
||||
// a) Check if the word matches the candidate entirely, in an case insensitive or
|
||||
// sensitive manner. If it does, return that there was an exact match.
|
||||
//
|
||||
// b) Check if the word is a prefix of the candidate, in a case insensitive or
|
||||
// sensitive manner. If it does, return that there was a prefix match.
|
||||
//
|
||||
// c) If the word is entirely lowercase, then check if it is contained anywhere in the
|
||||
// candidate in a case insensitive manner. If so, return that there was a substring
|
||||
// match.
|
||||
//
|
||||
// Note: We only have a substring match if the lowercase part is prefix match of
|
||||
// some word part. That way we don't match something like 'Class' when the user
|
||||
// types 'a'. But we would match 'FooAttribute' (since 'Attribute' starts with
|
||||
// 'a').
|
||||
//
|
||||
// d) If the word was not entirely lowercase, then check if it is contained in the
|
||||
// candidate in a case *sensitive* manner. If so, return that there was a substring
|
||||
// match.
|
||||
//
|
||||
// e) If the word was not entirely lowercase, then attempt a camel cased match as
|
||||
// well.
|
||||
//
|
||||
// f) The word is all lower case. Is it a case insensitive substring of the candidate starting
|
||||
// on a part boundary of the candidate?
|
||||
//
|
||||
// Only if all words have some sort of match is the pattern considered matched.
|
||||
|
||||
var subWordTextChunks = segment.subWordTextChunks;
|
||||
var matches: PatternMatch[] = undefined;
|
||||
|
||||
for (var i = 0, n = subWordTextChunks.length; i < n; i++) {
|
||||
var subWordTextChunk = subWordTextChunks[i];
|
||||
|
||||
// Try to match the candidate with this word
|
||||
var result = matchTextChunk(candidate, subWordTextChunk, /*punctuationStripped:*/ true);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
matches = matches || [];
|
||||
matches.push(result);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
function partStartsWith(candidate: string, candidateSpan: TextSpan, pattern: string, ignoreCase: boolean, patternSpan?: TextSpan): boolean {
|
||||
var patternPartStart = patternSpan ? patternSpan.start : 0;
|
||||
var patternPartLength = patternSpan ? patternSpan.length : pattern.length;
|
||||
|
||||
if (patternPartLength > candidateSpan.length) {
|
||||
// Pattern part is longer than the candidate part. There can never be a match.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ignoreCase) {
|
||||
for (var i = 0; i < patternPartLength; i++) {
|
||||
var ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
var ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
if (toLowerCase(ch1) !== toLowerCase(ch2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < patternPartLength; i++) {
|
||||
var ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
var ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
if (ch1 !== ch2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function tryCamelCaseMatch(candidate: string, candidateParts: TextSpan[], chunk: TextChunk, ignoreCase: boolean): number {
|
||||
var chunkCharacterSpans = chunk.characterSpans;
|
||||
|
||||
// Note: we may have more pattern parts than candidate parts. This is because multiple
|
||||
// pattern parts may match a candidate part. For example "SiUI" against "SimpleUI".
|
||||
// We'll have 3 pattern parts Si/U/I against two candidate parts Simple/UI. However, U
|
||||
// and I will both match in UI.
|
||||
|
||||
var currentCandidate = 0;
|
||||
var currentChunkSpan = 0;
|
||||
var firstMatch: number = undefined;
|
||||
var contiguous: boolean = undefined;
|
||||
|
||||
while (true) {
|
||||
// Let's consider our termination cases
|
||||
if (currentChunkSpan === chunkCharacterSpans.length) {
|
||||
// We did match! We shall assign a weight to this
|
||||
var weight = 0;
|
||||
|
||||
// Was this contiguous?
|
||||
if (contiguous) {
|
||||
weight += 1;
|
||||
}
|
||||
|
||||
// Did we start at the beginning of the candidate?
|
||||
if (firstMatch === 0) {
|
||||
weight += 2;
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
else if (currentCandidate === candidateParts.length) {
|
||||
// No match, since we still have more of the pattern to hit
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var candidatePart = candidateParts[currentCandidate];
|
||||
var gotOneMatchThisCandidate = false;
|
||||
|
||||
// Consider the case of matching SiUI against SimpleUIElement. The candidate parts
|
||||
// will be Simple/UI/Element, and the pattern parts will be Si/U/I. We'll match 'Si'
|
||||
// against 'Simple' first. Then we'll match 'U' against 'UI'. However, we want to
|
||||
// still keep matching pattern parts against that candidate part.
|
||||
for (; currentChunkSpan < chunkCharacterSpans.length; currentChunkSpan++) {
|
||||
var chunkCharacterSpan = chunkCharacterSpans[currentChunkSpan];
|
||||
|
||||
if (gotOneMatchThisCandidate) {
|
||||
// We've already gotten one pattern part match in this candidate. We will
|
||||
// only continue trying to consumer pattern parts if the last part and this
|
||||
// part are both upper case.
|
||||
if (!isUpperCaseLetter(chunk.text.charCodeAt(chunkCharacterSpans[currentChunkSpan - 1].start)) ||
|
||||
!isUpperCaseLetter(chunk.text.charCodeAt(chunkCharacterSpans[currentChunkSpan].start))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!partStartsWith(candidate, candidatePart, chunk.text, ignoreCase, chunkCharacterSpan)) {
|
||||
break;
|
||||
}
|
||||
|
||||
gotOneMatchThisCandidate = true;
|
||||
|
||||
firstMatch = firstMatch === undefined ? currentCandidate : firstMatch;
|
||||
|
||||
// If we were contiguous, then keep that value. If we weren't, then keep that
|
||||
// value. If we don't know, then set the value to 'true' as an initial match is
|
||||
// obviously contiguous.
|
||||
contiguous = contiguous === undefined ? true : contiguous;
|
||||
|
||||
candidatePart = createTextSpan(candidatePart.start + chunkCharacterSpan.length, candidatePart.length - chunkCharacterSpan.length);
|
||||
}
|
||||
|
||||
// Check if we matched anything at all. If we didn't, then we need to unset the
|
||||
// contiguous bit if we currently had it set.
|
||||
// If we haven't set the bit yet, then that means we haven't matched anything so
|
||||
// far, and we don't want to change that.
|
||||
if (!gotOneMatchThisCandidate && contiguous !== undefined) {
|
||||
contiguous = false;
|
||||
}
|
||||
|
||||
// Move onto the next candidate.
|
||||
currentCandidate++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to compare two matches to determine which is better. Matches are first
|
||||
// ordered by kind (so all prefix matches always beat all substring matches). Then, if the
|
||||
// match is a camel case match, the relative weights of hte match are used to determine
|
||||
// which is better (with a greater weight being better). Then if the match is of the same
|
||||
// type, then a case sensitive match is considered better than an insensitive one.
|
||||
function patternMatchCompareTo(match1: PatternMatch, match2: PatternMatch): number {
|
||||
return compareType(match1, match2) ||
|
||||
compareCamelCase(match1, match2) ||
|
||||
compareCase(match1, match2) ||
|
||||
comparePunctuation(match1, match2);
|
||||
}
|
||||
|
||||
function comparePunctuation(result1: PatternMatch, result2: PatternMatch) {
|
||||
// Consider a match to be better if it was successful without stripping punctuation
|
||||
// versus a match that had to strip punctuation to succeed.
|
||||
if (result1.punctuationStripped !== result2.punctuationStripped) {
|
||||
return result1.punctuationStripped ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareCase(result1: PatternMatch, result2: PatternMatch) {
|
||||
if (result1.isCaseSensitive !== result2.isCaseSensitive) {
|
||||
return result1.isCaseSensitive ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareType(result1: PatternMatch, result2: PatternMatch) {
|
||||
return result1.kind - result2.kind;
|
||||
}
|
||||
|
||||
function compareCamelCase(result1: PatternMatch, result2: PatternMatch) {
|
||||
if (result1.kind === PatternMatchKind.CamelCase && result2.kind === PatternMatchKind.CamelCase) {
|
||||
// Swap the values here. If result1 has a higher weight, then we want it to come
|
||||
// first.
|
||||
return result2.camelCaseWeight - result1.camelCaseWeight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function createSegment(text: string): Segment {
|
||||
return {
|
||||
totalTextChunk: createTextChunk(text),
|
||||
subWordTextChunks: breakPatternIntoTextChunks(text)
|
||||
}
|
||||
}
|
||||
|
||||
// A segment is considered invalid if we couldn't find any words in it.
|
||||
function segmentIsInvalid(segment: Segment) {
|
||||
return segment.subWordTextChunks.length === 0;
|
||||
}
|
||||
|
||||
function isUpperCaseLetter(ch: number) {
|
||||
// Fast check for the ascii range.
|
||||
if (ch >= CharacterCodes.A && ch <= CharacterCodes.Z) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ch < CharacterCodes.maxAsciiCharacter || !isUnicodeIdentifierStart(ch, ScriptTarget.Latest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
var str = String.fromCharCode(ch);
|
||||
return str === str.toUpperCase();
|
||||
}
|
||||
|
||||
function isLowerCaseLetter(ch: number) {
|
||||
// Fast check for the ascii range.
|
||||
if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ch < CharacterCodes.maxAsciiCharacter || !isUnicodeIdentifierStart(ch, ScriptTarget.Latest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
var str = String.fromCharCode(ch);
|
||||
return str === str.toLowerCase();
|
||||
}
|
||||
|
||||
function containsUpperCaseLetter(string: string): boolean {
|
||||
for (var i = 0, n = string.length; i < n; i++) {
|
||||
if (isUpperCaseLetter(string.charCodeAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function startsWith(string: string, search: string) {
|
||||
for (var i = 0, n = search.length; i < n; i++) {
|
||||
if (string.charCodeAt(i) !== search.charCodeAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assumes 'value' is already lowercase.
|
||||
function indexOfIgnoringCase(string: string, value: string): number {
|
||||
for (var i = 0, n = string.length - value.length; i <= n; i++) {
|
||||
if (startsWithIgnoringCase(string, value, i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Assumes 'value' is already lowercase.
|
||||
function startsWithIgnoringCase(string: string, value: string, start: number): boolean {
|
||||
for (var i = 0, n = value.length; i < n; i++) {
|
||||
var ch1 = toLowerCase(string.charCodeAt(i + start));
|
||||
var ch2 = value.charCodeAt(i);
|
||||
|
||||
if (ch1 !== ch2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function toLowerCase(ch: number): number {
|
||||
// Fast convert for the ascii range.
|
||||
if (ch >= CharacterCodes.A && ch <= CharacterCodes.Z) {
|
||||
return CharacterCodes.a + (ch - CharacterCodes.A);
|
||||
}
|
||||
|
||||
if (ch < CharacterCodes.maxAsciiCharacter) {
|
||||
return ch;
|
||||
}
|
||||
|
||||
// TODO: find a way to compute this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
return String.fromCharCode(ch).toLowerCase().charCodeAt(0);
|
||||
}
|
||||
|
||||
function isDigit(ch: number) {
|
||||
// TODO(cyrusn): Find a way to support this for unicode digits.
|
||||
return ch >= CharacterCodes._0 && ch <= CharacterCodes._9;
|
||||
}
|
||||
|
||||
function isWordChar(ch: number) {
|
||||
return isUpperCaseLetter(ch) || isLowerCaseLetter(ch) || isDigit(ch) || ch === CharacterCodes._ || ch === CharacterCodes.$;
|
||||
}
|
||||
|
||||
function breakPatternIntoTextChunks(pattern: string): TextChunk[] {
|
||||
var result: TextChunk[] = [];
|
||||
var wordStart = 0;
|
||||
var wordLength = 0;
|
||||
|
||||
for (var i = 0; i < pattern.length; i++) {
|
||||
var ch = pattern.charCodeAt(i);
|
||||
if (isWordChar(ch)) {
|
||||
if (wordLength++ === 0) {
|
||||
wordStart = i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (wordLength > 0) {
|
||||
result.push(createTextChunk(pattern.substr(wordStart, wordLength)));
|
||||
wordLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wordLength > 0) {
|
||||
result.push(createTextChunk(pattern.substr(wordStart, wordLength)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function createTextChunk(text: string): TextChunk {
|
||||
var textLowerCase = text.toLowerCase();
|
||||
return {
|
||||
text,
|
||||
textLowerCase,
|
||||
isLowerCase: text === textLowerCase,
|
||||
characterSpans: breakIntoCharacterSpans(text)
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */ export function breakIntoCharacterSpans(identifier: string): TextSpan[] {
|
||||
return breakIntoSpans(identifier, /*word:*/ false);
|
||||
}
|
||||
|
||||
/* @internal */ export function breakIntoWordSpans(identifier: string): TextSpan[] {
|
||||
return breakIntoSpans(identifier, /*word:*/ true);
|
||||
}
|
||||
|
||||
function breakIntoSpans(identifier: string, word: boolean): TextSpan[] {
|
||||
var result: TextSpan[] = [];
|
||||
|
||||
var wordStart = 0;
|
||||
for (var i = 1, n = identifier.length; i < n; i++) {
|
||||
var lastIsDigit = isDigit(identifier.charCodeAt(i - 1));
|
||||
var currentIsDigit = isDigit(identifier.charCodeAt(i));
|
||||
|
||||
var hasTransitionFromLowerToUpper = transitionFromLowerToUpper(identifier, word, i);
|
||||
var hasTransitionFromUpperToLower = transitionFromUpperToLower(identifier, word, i, wordStart);
|
||||
|
||||
if (charIsPunctuation(identifier.charCodeAt(i - 1)) ||
|
||||
charIsPunctuation(identifier.charCodeAt(i)) ||
|
||||
lastIsDigit != currentIsDigit ||
|
||||
hasTransitionFromLowerToUpper ||
|
||||
hasTransitionFromUpperToLower) {
|
||||
|
||||
if (!isAllPunctuation(identifier, wordStart, i)) {
|
||||
result.push(createTextSpan(wordStart, i - wordStart));
|
||||
}
|
||||
|
||||
wordStart = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAllPunctuation(identifier, wordStart, identifier.length)) {
|
||||
result.push(createTextSpan(wordStart, identifier.length - wordStart));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function charIsPunctuation(ch: number) {
|
||||
switch (ch) {
|
||||
case CharacterCodes.exclamation:
|
||||
case CharacterCodes.doubleQuote:
|
||||
case CharacterCodes.hash:
|
||||
case CharacterCodes.percent:
|
||||
case CharacterCodes.ampersand:
|
||||
case CharacterCodes.singleQuote:
|
||||
case CharacterCodes.openParen:
|
||||
case CharacterCodes.closeParen:
|
||||
case CharacterCodes.asterisk:
|
||||
case CharacterCodes.comma:
|
||||
case CharacterCodes.minus:
|
||||
case CharacterCodes.dot:
|
||||
case CharacterCodes.slash:
|
||||
case CharacterCodes.colon:
|
||||
case CharacterCodes.semicolon:
|
||||
case CharacterCodes.question:
|
||||
case CharacterCodes.at:
|
||||
case CharacterCodes.openBracket:
|
||||
case CharacterCodes.backslash:
|
||||
case CharacterCodes.closeBracket:
|
||||
case CharacterCodes._:
|
||||
case CharacterCodes.openBrace:
|
||||
case CharacterCodes.closeBrace:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isAllPunctuation(identifier: string, start: number, end: number): boolean {
|
||||
for (var i = start; i < end; i++) {
|
||||
var ch = identifier.charCodeAt(i);
|
||||
|
||||
// We don't consider _ or $ as punctuation as there may be things with that name.
|
||||
if (!charIsPunctuation(ch) || ch === CharacterCodes._ || ch === CharacterCodes.$) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function transitionFromUpperToLower(identifier: string, word: boolean, index: number, wordStart: number): boolean {
|
||||
if (word) {
|
||||
// Cases this supports:
|
||||
// 1) IDisposable -> I, Disposable
|
||||
// 2) UIElement -> UI, Element
|
||||
// 3) HTMLDocument -> HTML, Document
|
||||
//
|
||||
// etc.
|
||||
if (index != wordStart &&
|
||||
index + 1 < identifier.length) {
|
||||
var currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
var nextIsLower = isLowerCaseLetter(identifier.charCodeAt(index + 1));
|
||||
|
||||
if (currentIsUpper && nextIsLower) {
|
||||
// We have a transition from an upper to a lower letter here. But we only
|
||||
// want to break if all the letters that preceded are uppercase. i.e. if we
|
||||
// have "Foo" we don't want to break that into "F, oo". But if we have
|
||||
// "IFoo" or "UIFoo", then we want to break that into "I, Foo" and "UI,
|
||||
// Foo". i.e. the last uppercase letter belongs to the lowercase letters
|
||||
// that follows. Note: this will make the following not split properly:
|
||||
// "HELLOthere". However, these sorts of names do not show up in .Net
|
||||
// programs.
|
||||
for (var i = wordStart; i < index; i++) {
|
||||
if (!isUpperCaseLetter(identifier.charCodeAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function transitionFromLowerToUpper(identifier: string, word: boolean, index: number): boolean {
|
||||
var lastIsUpper = isUpperCaseLetter(identifier.charCodeAt(index - 1));
|
||||
var currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
|
||||
// See if the casing indicates we're starting a new word. Note: if we're breaking on
|
||||
// words, then just seeing an upper case character isn't enough. Instead, it has to
|
||||
// be uppercase and the previous character can't be uppercase.
|
||||
//
|
||||
// For example, breaking "AddMetadata" on words would make: Add Metadata
|
||||
//
|
||||
// on characters would be: A dd M etadata
|
||||
//
|
||||
// Break "AM" on words would be: AM
|
||||
//
|
||||
// on characters would be: A M
|
||||
//
|
||||
// We break the search string on characters. But we break the symbol name on words.
|
||||
var transition = word
|
||||
? (currentIsUpper && !lastIsUpper)
|
||||
: currentIsUpper;
|
||||
return transition;
|
||||
}
|
||||
}
|
||||
+86
-167
@@ -2,14 +2,15 @@
|
||||
|
||||
/// <reference path='breakpoints.ts' />
|
||||
/// <reference path='outliningElementsCollector.ts' />
|
||||
/// <reference path='navigateTo.ts' />
|
||||
/// <reference path='navigationBar.ts' />
|
||||
/// <reference path='patternMatcher.ts' />
|
||||
/// <reference path='signatureHelp.ts' />
|
||||
/// <reference path='utilities.ts' />
|
||||
/// <reference path='formatting\formatting.ts' />
|
||||
/// <reference path='formatting\smartIndenter.ts' />
|
||||
|
||||
module ts {
|
||||
|
||||
export var servicesVersion = "0.4"
|
||||
|
||||
export interface Node {
|
||||
@@ -61,9 +62,9 @@ module ts {
|
||||
scriptSnapshot: IScriptSnapshot;
|
||||
nameTable: Map<string>;
|
||||
getNamedDeclarations(): Declaration[];
|
||||
getLineAndCharacterFromPosition(pos: number): LineAndCharacter;
|
||||
getLineAndCharacterOfPosition(pos: number): LineAndCharacter;
|
||||
getLineStarts(): number[];
|
||||
getPositionFromLineAndCharacter(line: number, character: number): number;
|
||||
getPositionOfLineAndCharacter(line: number, character: number): number;
|
||||
update(newText: string, textChangeRange: TextChangeRange): SourceFile;
|
||||
}
|
||||
|
||||
@@ -612,7 +613,7 @@ module ts {
|
||||
}
|
||||
|
||||
if (paramHelpStringMargin === undefined) {
|
||||
paramHelpStringMargin = sourceFile.getLineAndCharacterFromPosition(firstLineParamHelpStringPos).character - 1;
|
||||
paramHelpStringMargin = sourceFile.getLineAndCharacterOfPosition(firstLineParamHelpStringPos).character;
|
||||
}
|
||||
|
||||
// Now consume white spaces max
|
||||
@@ -750,16 +751,16 @@ module ts {
|
||||
return updateSourceFile(this, newText, textChangeRange);
|
||||
}
|
||||
|
||||
public getLineAndCharacterFromPosition(position: number): LineAndCharacter {
|
||||
return getLineAndCharacterOfPosition(this, position);
|
||||
public getLineAndCharacterOfPosition(position: number): LineAndCharacter {
|
||||
return ts.getLineAndCharacterOfPosition(this, position);
|
||||
}
|
||||
|
||||
public getLineStarts(): number[] {
|
||||
return getLineStarts(this);
|
||||
}
|
||||
|
||||
public getPositionFromLineAndCharacter(line: number, character: number): number {
|
||||
return getPositionFromLineAndCharacter(this, line, character);
|
||||
public getPositionOfLineAndCharacter(line: number, character: number): number {
|
||||
return ts.getPositionOfLineAndCharacter(this, line, character);
|
||||
}
|
||||
|
||||
public getNamedDeclarations() {
|
||||
@@ -900,7 +901,7 @@ module ts {
|
||||
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
|
||||
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
|
||||
|
||||
getNavigateToItems(searchValue: string): NavigateToItem[];
|
||||
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
|
||||
getNavigationBarItems(fileName: string): NavigationBarItem[];
|
||||
|
||||
getOutliningSpans(fileName: string): OutliningSpan[];
|
||||
@@ -991,6 +992,7 @@ module ts {
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
|
||||
PlaceOpenBraceOnNewLineForFunctions: boolean;
|
||||
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
|
||||
[s: string]: boolean | number| string;
|
||||
}
|
||||
|
||||
export interface DefinitionInfo {
|
||||
@@ -1377,13 +1379,6 @@ module ts {
|
||||
public static typeAlias = "type alias name";
|
||||
}
|
||||
|
||||
enum MatchKind {
|
||||
none = 0,
|
||||
exact = 1,
|
||||
substring = 2,
|
||||
prefix = 3
|
||||
}
|
||||
|
||||
/// Language Service
|
||||
|
||||
interface CompletionSession {
|
||||
@@ -1991,6 +1986,62 @@ module ts {
|
||||
});
|
||||
}
|
||||
|
||||
/* @internal */ export function getContainerNode(node: Node): Node {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */ export function getNodeKind(node: Node): string {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
|
||||
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
|
||||
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
|
||||
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
|
||||
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return isConst(node)
|
||||
? ScriptElementKind.constElement
|
||||
: isLet(node)
|
||||
? ScriptElementKind.letElement
|
||||
: ScriptElementKind.variableElement;
|
||||
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
|
||||
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
|
||||
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
return ScriptElementKind.memberFunctionElement;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
return ScriptElementKind.memberVariableElement;
|
||||
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
|
||||
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
|
||||
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
|
||||
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
|
||||
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
|
||||
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
|
||||
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
|
||||
}
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
|
||||
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry = createDocumentRegistry()): LanguageService {
|
||||
var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
|
||||
var ruleProvider: formatting.RulesProvider;
|
||||
@@ -2721,29 +2772,6 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getContainerNode(node: Node): Node {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(drosen): use contextual SemanticMeaning.
|
||||
function getSymbolKind(symbol: Symbol, typeResolver: TypeChecker, location: Node): string {
|
||||
var flags = symbol.getFlags();
|
||||
@@ -2830,39 +2858,6 @@ module ts {
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
|
||||
function getNodeKind(node: Node): string {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
|
||||
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
|
||||
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
|
||||
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
|
||||
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return isConst(node)
|
||||
? ScriptElementKind.constElement
|
||||
: isLet(node)
|
||||
? ScriptElementKind.letElement
|
||||
: ScriptElementKind.variableElement;
|
||||
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
|
||||
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
|
||||
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
return ScriptElementKind.memberFunctionElement;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
return ScriptElementKind.memberVariableElement;
|
||||
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
|
||||
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
|
||||
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
|
||||
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
|
||||
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
|
||||
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
|
||||
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
|
||||
}
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
|
||||
function getSymbolModifiers(symbol: Symbol): string {
|
||||
return symbol && symbol.declarations && symbol.declarations.length > 0
|
||||
? getNodeModifiers(symbol.declarations[0])
|
||||
@@ -3441,7 +3436,9 @@ module ts {
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ForKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.ForStatement) || hasKind(node.parent, SyntaxKind.ForInStatement)) {
|
||||
if (hasKind(node.parent, SyntaxKind.ForStatement) ||
|
||||
hasKind(node.parent, SyntaxKind.ForInStatement) ||
|
||||
hasKind(node.parent, SyntaxKind.ForOfStatement)) {
|
||||
return getLoopBreakContinueOccurrences(<IterationStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
@@ -3718,6 +3715,7 @@ module ts {
|
||||
switch (owner.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
return getLoopBreakContinueOccurrences(<IterationStatement>owner)
|
||||
@@ -3762,6 +3760,7 @@ module ts {
|
||||
// Fall through.
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
if (!statement.label || isLabeledBy(node, statement.label.text)) {
|
||||
@@ -4649,7 +4648,7 @@ module ts {
|
||||
return true;
|
||||
}
|
||||
else if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).left === node) {
|
||||
var operator = (<BinaryExpression>parent).operator;
|
||||
var operator = (<BinaryExpression>parent).operatorToken.kind;
|
||||
return SyntaxKind.FirstAssignment <= operator && operator <= SyntaxKind.LastAssignment;
|
||||
}
|
||||
}
|
||||
@@ -4658,89 +4657,10 @@ module ts {
|
||||
}
|
||||
|
||||
/// NavigateTo
|
||||
function getNavigateToItems(searchValue: string): NavigateToItem[] {
|
||||
function getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[] {
|
||||
synchronizeHostData();
|
||||
|
||||
// Split search value in terms array
|
||||
var terms = searchValue.split(" ");
|
||||
|
||||
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
|
||||
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
|
||||
|
||||
var items: NavigateToItem[] = [];
|
||||
|
||||
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
var fileName = sourceFile.fileName;
|
||||
var declarations = sourceFile.getNamedDeclarations();
|
||||
for (var i = 0, n = declarations.length; i < n; i++) {
|
||||
var declaration = declarations[i];
|
||||
// TODO(jfreeman): Skip this declaration if it has a computed name
|
||||
var name = (<Identifier>declaration.name).text;
|
||||
var matchKind = getMatchKind(searchTerms, name);
|
||||
if (matchKind !== MatchKind.none) {
|
||||
var container = <Declaration>getContainerNode(declaration);
|
||||
items.push({
|
||||
name: name,
|
||||
kind: getNodeKind(declaration),
|
||||
kindModifiers: getNodeModifiers(declaration),
|
||||
matchKind: MatchKind[matchKind],
|
||||
fileName: fileName,
|
||||
textSpan: createTextSpanFromBounds(declaration.getStart(), declaration.getEnd()),
|
||||
// TODO(jfreeman): What should be the containerName when the container has a computed name?
|
||||
containerName: container && container.name ? (<Identifier>container.name).text : "",
|
||||
containerKind: container && container.name ? getNodeKind(container) : ""
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
|
||||
function hasAnyUpperCaseCharacter(s: string): boolean {
|
||||
for (var i = 0, n = s.length; i < n; i++) {
|
||||
var c = s.charCodeAt(i);
|
||||
if ((CharacterCodes.A <= c && c <= CharacterCodes.Z) ||
|
||||
(c >= CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
|
||||
var matchKind = MatchKind.none;
|
||||
|
||||
if (name) {
|
||||
for (var j = 0, n = searchTerms.length; j < n; j++) {
|
||||
var searchTerm = searchTerms[j];
|
||||
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
|
||||
// in case of case-insensitive search searchTerm.term will already be lower-cased
|
||||
var index = nameToSearch.indexOf(searchTerm.term);
|
||||
if (index < 0) {
|
||||
// Didn't match.
|
||||
return MatchKind.none;
|
||||
}
|
||||
|
||||
var termKind = MatchKind.substring;
|
||||
if (index === 0) {
|
||||
// here we know that match occur at the beginning of the string.
|
||||
// if search term and declName has the same length - we have an exact match, otherwise declName have longer length and this will be prefix match
|
||||
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
|
||||
}
|
||||
|
||||
// Update our match kind if we don't have one, or if this match is better.
|
||||
if (matchKind === MatchKind.none || termKind < matchKind) {
|
||||
matchKind = termKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchKind;
|
||||
}
|
||||
return ts.NavigateTo.getNavigateToItems(program, cancellationToken, searchValue, maxResultCount);
|
||||
}
|
||||
|
||||
function containErrors(diagnostics: Diagnostic[]): boolean {
|
||||
@@ -5540,11 +5460,13 @@ module ts {
|
||||
var declarations = symbol.getDeclarations();
|
||||
if (declarations && declarations.length > 0) {
|
||||
// Disallow rename for elements that are defined in the standard TypeScript library.
|
||||
var defaultLibFile = getDefaultLibFileName(host.getCompilationSettings());
|
||||
for (var i = 0; i < declarations.length; i++) {
|
||||
var sourceFile = declarations[i].getSourceFile();
|
||||
if (sourceFile && endsWith(sourceFile.fileName, defaultLibFile)) {
|
||||
return getRenameInfoError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library.key));
|
||||
var defaultLibFileName = host.getDefaultLibFileName(host.getCompilationSettings());
|
||||
if (defaultLibFileName) {
|
||||
for (var i = 0; i < declarations.length; i++) {
|
||||
var sourceFile = declarations[i].getSourceFile();
|
||||
if (sourceFile && getCanonicalFileName(ts.normalizePath(sourceFile.fileName)) === getCanonicalFileName(ts.normalizePath(defaultLibFileName))) {
|
||||
return getRenameInfoError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library.key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5566,10 +5488,6 @@ module ts {
|
||||
|
||||
return getRenameInfoError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_this_element.key));
|
||||
|
||||
function endsWith(string: string, value: string): boolean {
|
||||
return string.lastIndexOf(value) + value.length === string.length;
|
||||
}
|
||||
|
||||
function getRenameInfoError(localizedErrorMessage: string): RenameInfo {
|
||||
return {
|
||||
canRename: false,
|
||||
@@ -5680,7 +5598,7 @@ module ts {
|
||||
keyword2 === SyntaxKind.ConstructorKeyword ||
|
||||
keyword2 === SyntaxKind.StaticKeyword) {
|
||||
|
||||
// Allow things like "public get", "public constructor" and "public static".
|
||||
// Allow things like "public get", "public constructor" and "public static".
|
||||
// These are all legal.
|
||||
return true;
|
||||
}
|
||||
@@ -5697,7 +5615,7 @@ module ts {
|
||||
|
||||
// If there is a syntactic classifier ('syntacticClassifierAbsent' is false),
|
||||
// we will be more conservative in order to avoid conflicting with the syntactic classifier.
|
||||
function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent?: boolean): ClassificationResult {
|
||||
function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): ClassificationResult {
|
||||
var offset = 0;
|
||||
var token = SyntaxKind.Unknown;
|
||||
var lastNonTriviaToken = SyntaxKind.Unknown;
|
||||
@@ -5799,7 +5717,8 @@ module ts {
|
||||
else if (token === SyntaxKind.AnyKeyword ||
|
||||
token === SyntaxKind.StringKeyword ||
|
||||
token === SyntaxKind.NumberKeyword ||
|
||||
token === SyntaxKind.BooleanKeyword) {
|
||||
token === SyntaxKind.BooleanKeyword ||
|
||||
token === SyntaxKind.SymbolKeyword) {
|
||||
if (angleBracketStack > 0 && !syntacticClassifierAbsent) {
|
||||
// If it looks like we're could be in something generic, don't classify this
|
||||
// as a keyword. We may just get overwritten by the syntactic classifier,
|
||||
|
||||
@@ -138,7 +138,7 @@ module ts {
|
||||
* Returns a JSON-encoded value of the type:
|
||||
* { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; textSpan: { start: number; length: number}; } [] = [];
|
||||
*/
|
||||
getNavigateToItems(searchValue: string): string;
|
||||
getNavigateToItems(searchValue: string, maxResultCount?: number): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
@@ -274,10 +274,7 @@ module ts {
|
||||
}
|
||||
|
||||
public getDefaultLibFileName(options: CompilerOptions): string {
|
||||
// Shim the API changes for 1.5 release. This should be removed once
|
||||
// TypeScript 1.5 has shipped.
|
||||
return "";
|
||||
//return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
|
||||
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,11 +628,11 @@ module ts {
|
||||
/// NAVIGATE TO
|
||||
|
||||
/** Return a list of symbols that are interesting to navigate to */
|
||||
public getNavigateToItems(searchValue: string): string {
|
||||
public getNavigateToItems(searchValue: string, maxResultCount?: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getNavigateToItems('" + searchValue + "')",
|
||||
"getNavigateToItems('" + searchValue + "', " + maxResultCount+ ")",
|
||||
() => {
|
||||
var items = this.languageService.getNavigateToItems(searchValue);
|
||||
var items = this.languageService.getNavigateToItems(searchValue, maxResultCount);
|
||||
return items;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,12 +6,11 @@ module ts {
|
||||
}
|
||||
|
||||
export function getEndLinePosition(line: number, sourceFile: SourceFile): number {
|
||||
Debug.assert(line >= 1);
|
||||
Debug.assert(line >= 0);
|
||||
var lineStarts = sourceFile.getLineStarts();
|
||||
|
||||
// lines returned by SourceFile.getLineAndCharacterForPosition are 1-based
|
||||
var lineIndex = line - 1;
|
||||
if (lineIndex === lineStarts.length - 1) {
|
||||
var lineIndex = line;
|
||||
if (lineIndex + 1 === lineStarts.length) {
|
||||
// last line - return EOF
|
||||
return sourceFile.text.length - 1;
|
||||
}
|
||||
@@ -32,15 +31,10 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function getStartPositionOfLine(line: number, sourceFile: SourceFile): number {
|
||||
Debug.assert(line >= 1);
|
||||
return sourceFile.getLineStarts()[line - 1];
|
||||
}
|
||||
|
||||
export function getStartLinePositionForPosition(position: number, sourceFile: SourceFile): number {
|
||||
export function getLineStartPositionForPosition(position: number, sourceFile: SourceFile): number {
|
||||
var lineStarts = sourceFile.getLineStarts();
|
||||
var line = sourceFile.getLineAndCharacterFromPosition(position).line;
|
||||
return lineStarts[line - 1];
|
||||
var line = sourceFile.getLineAndCharacterOfPosition(position).line;
|
||||
return lineStarts[line];
|
||||
}
|
||||
|
||||
export function rangeContainsRange(r1: TextRange, r2: TextRange): boolean {
|
||||
|
||||
Reference in New Issue
Block a user