def test_default_completion_items(self): """ Test that the completion handler returns a set of default values when not connected to any URI """ # If: The script file exists input_text = 'create tab' doc_position = TextDocumentPosition.from_dict({ 'text_document': { 'uri': self.default_uri }, 'position': { 'line': 0, 'character': 10 # end of 'tab' word } }) context: RequestContext = utils.MockRequestContext() config = Configuration() config.sql.intellisense.enable_intellisense = True self.mock_workspace_service._configuration = config workspace, script_file = self._get_test_workspace(True, input_text) self.mock_workspace_service._workspace = workspace service: LanguageService = self._init_service() service._valid_uri.add(doc_position.text_document.uri) # When: I request completion item service.handle_completion_request(context, doc_position) # Then: # ... An default completion set should be sent over the notification context.send_response.assert_called_once() completions: List[CompletionItem] = context.last_response_params self.assertTrue(len(completions) > 0) self.verify_match('TABLE', completions, Range.from_data(0, 7, 0, 10))
def test_format_doc_no_pgsql_format(self): """ Test that the format codepath succeeds even if the configuration options aren't defined """ input_text = 'select * from foo where id in (select id from bar);' context: RequestContext = utils.MockRequestContext() self.mock_workspace_service._configuration = None workspace, script_file = self._get_test_workspace(True, input_text) self.mock_workspace_service._workspace = workspace service: LanguageService = self._init_service() format_options = FormattingOptions() format_options.insert_spaces = False format_params = DocumentFormattingParams() format_params.options = format_options format_params.text_document = self.default_text_document_id # add uri to valid uri set ensure request passes uri check # normally done in flavor change handler, but we are not testing that here service._valid_uri.add(format_params.text_document.uri) # When: I have no useful formatting defaults defined service.handle_doc_format_request(context, format_params) # Then: # ... There should be no changes to the doc context.send_response.assert_called_once() edits: List[TextEdit] = context.last_response_params self.assertTrue(len(edits) > 0) self.assert_range_equals(edits[0].range, Range.from_data(0, 0, 0, len(input_text))) self.assertEqual(edits[0].new_text, input_text)
def test_get_lines_in_range_invalid_start(self): # Setup: Create a script file with a selection of test text sf = self._get_test_script_file() with self.assertRaises(ValueError): # If: I ask for the lines of a file that have an invalid start # Then: I should get an exception params = Range.from_data(-1, 200, 2, 3) sf.get_lines_in_range(params)
def test_get_lines_in_range_valid(self): # Setup: Create a script file with a selection of test text sf = self._get_test_script_file() # If: I ask for the valid lines of the file params = Range.from_data(1, 1, 3, 1) result = sf.get_lines_in_range(params) # Then: I should get a set of lines with the expected result expected_result = ['ef', 'ghij', 'k'] self.assertEqual(result, expected_result)
def test_get_text_in_range_start_of_line(self): """Test that get_text_in_range works when the cursor is at the start of a line""" sf = self._get_test_script_file() # If I get text from a range that covers the entire first line and ends on the second line before any text params = Range.from_data(0, 0, 1, 0) result = sf.get_text_in_range(params) # Then I should get the first line and a line with no content as the result expected_result = os.linesep.join(['abc', '']) self.assertEqual(result, expected_result)
def send_definition_using_connected_completions( self, request_context: RequestContext, scriptparseinfo: ScriptParseInfo, params: TextDocumentPosition, context: ConnectionContext) -> bool: if not context or not context.is_connected: return False definition_result: DefinitionResult = None completer: Completer = context.completer completions: List[Completion] = completer.get_completions( scriptparseinfo.document, None) if completions: word_under_cursor = scriptparseinfo.document.get_word_under_cursor( ) matching_completion = next( completion for completion in completions if completion.display == word_under_cursor) if matching_completion: connection = self._connection_service.get_connection( params.text_document.uri, ConnectionType.QUERY) scripter_instance = scripter.Scripter(connection) object_metadata = ObjectMetadata( None, None, matching_completion.display_meta, matching_completion.display, matching_completion.schema) create_script = scripter_instance.script( ScriptOperation.CREATE, object_metadata) if create_script: with tempfile.NamedTemporaryFile( mode='wt', delete=False, encoding='utf-8', suffix='.sql', newline=None) as namedfile: namedfile.write(create_script) if namedfile.name: file_uri = "file:///" + namedfile.name.strip('/') location_in_script = Location( file_uri, Range(Position(0, 1), Position(1, 1))) definition_result = DefinitionResult( False, None, [ location_in_script, ]) request_context.send_response( definition_result.locations) return True if definition_result is None: request_context.send_response(DefinitionResult(True, '', [])) return False
def test_format_mysql_doc_range(self): """ Test that the format document range codepath works as expected with a mysql doc """ # set up service provider with mysql connection self.mock_service_provider = ServiceProvider(self.mock_server, {}, MYSQL_PROVIDER_NAME, None) self.mock_service_provider._services[ constants.WORKSPACE_SERVICE_NAME] = self.mock_workspace_service self.mock_service_provider._services[ constants.CONNECTION_SERVICE_NAME] = self.mock_connection_service self.mock_service_provider._is_initialized = True # If: The script file doesn't exist (there is an empty workspace) input_lines: List[str] = [ 'select * from t1', 'select * from foo where id in (select id from bar);' ] input_text = '\n'.join(input_lines) expected_output = '\n'.join([ 'SELECT *', 'FROM foo', 'WHERE id IN', '\t\t\t\t(SELECT id', '\t\t\t\t\tFROM bar);' ]) context: RequestContext = utils.MockRequestContext() config = Configuration() config.my_sql = MySQLConfiguration() config.my_sql.format.keyword_case = 'upper' self.mock_workspace_service._configuration = config workspace, script_file = self._get_test_workspace(True, input_text) self.mock_workspace_service._workspace = workspace service: LanguageService = self._init_service() format_options = FormattingOptions() format_options.insert_spaces = False format_params = DocumentRangeFormattingParams() format_params.options = format_options format_params.text_document = self.default_text_document_id # add uri to valid uri set ensure request passes uri check # normally done in flavor change handler, but we are not testing that here service._valid_uri.add(format_params.text_document.uri) # When: I request format the 2nd line of a document format_params.range = Range.from_data(1, 0, 1, len(input_lines[1])) service.handle_doc_range_format_request(context, format_params) # Then: # ... only the 2nd line should be formatted context.send_response.assert_called_once() edits: List[TextEdit] = context.last_response_params self.assertTrue(len(edits) > 0) self.assert_range_equals(edits[0].range, format_params.range) self.assertEqual(edits[0].new_text, expected_output)
def test_get_text_selection(self): """Text the workspace service's public get_text method when getting a selection of the text of a file""" # Set up the service with a file workspace_service = WorkspaceService() file_uri = 'untitled:Test_file' file_text = os.linesep.join(['line1', 'line 2 content', ' line 3 ']) workspace_service._workspace.open_file(file_uri, file_text) # Retrieve the full text of the file and make sure it matches selection_range = Range(Position(1, 1), Position(2, 4)) result_text = workspace_service.get_text(file_uri, selection_range) self.assertEqual(result_text, os.linesep.join(['ine 2 content', ' lin']))
def test_get_matches_full_keyword(self): """Test get matches on a keyword""" # Given a default completion helper helper = DefaultCompletionHelper() text_range = Range.from_data(1, 1, 1, 2) start = 'create' # When I match 'create' matches: List[CompletionItem] = helper.get_matches( start, text_range, False) # Then I expect only 1 match self.assertEqual(1, len(matches)) # ... and I expect words to be uppercased self.verify_match('CREATE', matches, text_range)
def test_format_mysql_doc(self): """ Test that the format document codepath works as expected with a mysql doc """ # set up service provider with mysql connection self.mock_service_provider = ServiceProvider(self.mock_server, {}, MYSQL_PROVIDER_NAME, None) self.mock_service_provider._services[ constants.WORKSPACE_SERVICE_NAME] = self.mock_workspace_service self.mock_service_provider._services[ constants.CONNECTION_SERVICE_NAME] = self.mock_connection_service self.mock_service_provider._is_initialized = True # If: We have a basic string to be formatted input_text = 'select * from foo where id in (select id from bar);' # Note: sqlparse always uses '\n\ for line separator even on windows. # For now, respecting this behavior and leaving as-is expected_output = '\n'.join([ 'SELECT *', 'FROM foo', 'WHERE id IN', '\t\t\t\t(SELECT id', '\t\t\t\t\tFROM bar);' ]) context: RequestContext = utils.MockRequestContext() config = Configuration() config.my_sql = MySQLConfiguration() config.my_sql.format.keyword_case = 'upper' self.mock_workspace_service._configuration = config workspace, script_file = self._get_test_workspace(True, input_text) self.mock_workspace_service._workspace = workspace service: LanguageService = self._init_service() format_options = FormattingOptions() format_options.insert_spaces = False format_params = DocumentFormattingParams() format_params.options = format_options format_params.text_document = self.default_text_document_id # add uri to valid uri set ensure request passes uri check # normally done in flavor change handler, but we are not testing that here service._valid_uri.add(format_params.text_document.uri) # When: I request document formatting service.handle_doc_format_request(context, format_params) # Then: # ... The entire document text should be formatted context.send_response.assert_called_once() edits: List[TextEdit] = context.last_response_params self.assertTrue(len(edits) > 0) self.assert_range_equals(edits[0].range, Range.from_data(0, 0, 0, len(input_text))) self.assertEqual(edits[0].new_text, expected_output)
def handle_definition_request( self, request_context: RequestContext, text_document_position: TextDocumentPosition) -> None: request_context.send_notification( STATUS_CHANGE_NOTIFICATION, StatusChangeParams( owner_uri=text_document_position.text_document.uri, status="DefinitionRequested")) def do_send_default_empty_response(): request_context.send_response([]) if self.should_skip_intellisense( text_document_position.text_document.uri): do_send_default_empty_response() return script_file: ScriptFile = self._workspace_service.workspace.get_file( text_document_position.text_document.uri) if script_file is None: do_send_default_empty_response() return script_parse_info: ScriptParseInfo = self.get_script_parse_info( text_document_position.text_document.uri, create_if_not_exists=False) if not script_parse_info or not script_parse_info.can_queue(): do_send_default_empty_response() return cursor_position: int = len( script_file.get_text_in_range( Range.from_data(0, 0, text_document_position.position.line, text_document_position.position.character))) text: str = script_file.get_all_text() script_parse_info.document = Document(text, cursor_position) operation = QueuedOperation( script_parse_info.connection_key, functools.partial(self.send_definition_using_connected_completions, request_context, script_parse_info, text_document_position), functools.partial(do_send_default_empty_response)) self.operations_queue.add_operation(operation) request_context.send_notification( STATUS_CHANGE_NOTIFICATION, StatusChangeParams( owner_uri=text_document_position.text_document.uri, status="DefinitionRequestCompleted"))
def to_completion_item(cls, completion: Completion, params: TextDocumentPosition) -> CompletionItem: key = completion.text start_position = LanguageService._get_start_position( params.position, completion.start_position) text_range = Range(start=start_position, end=params.position) kind = DISPLAY_META_MAP.get(completion.display_meta, CompletionItemKind.Unit) completion_item = CompletionItem() completion_item.label = key completion_item.detail = completion.display completion_item.insert_text = key completion_item.kind = kind completion_item.text_edit = TextEdit.from_data(text_range, key) # Add a sort text to put keywords after all other items completion_item.sort_text = f'~{key}' if completion_item.kind == CompletionItemKind.Keyword else key return completion_item
def handle_completion_request(self, request_context: RequestContext, params: TextDocumentPosition) -> None: """ Lookup available completions when valid completion suggestions are requested. Sends an array of CompletionItem objects over the wire """ response = [] def do_send_default_empty_response(): request_context.send_response(response) script_file: ScriptFile = self._workspace_service.workspace.get_file( params.text_document.uri) if script_file is None: do_send_default_empty_response() return if self.should_skip_intellisense(script_file.file_uri): do_send_default_empty_response() return script_parse_info: ScriptParseInfo = self.get_script_parse_info( params.text_document.uri, create_if_not_exists=False) if not script_parse_info or not script_parse_info.can_queue(): self._send_default_completions(request_context, script_file, params) else: cursor_position: int = len( script_file.get_text_in_range( Range.from_data(0, 0, params.position.line, params.position.character))) text: str = script_file.get_all_text() script_parse_info.document = Document(text, cursor_position) operation = QueuedOperation( script_parse_info.connection_key, functools.partial(self.send_connected_completions, request_context, script_parse_info, params), functools.partial(self._send_default_completions, request_context, script_file, params)) self.operations_queue.add_operation(operation)
def test_get_matches_one_char(self): """Test get matches on 1 character range""" # Given a default completion helper helper = DefaultCompletionHelper() text_range = Range.from_data(1, 1, 1, 2) start = 'c' # When I match 'c' matches: List[CompletionItem] = helper.get_matches( start, text_range, False) # Then I expect keywords starting with c to be returned self.assertTrue(len(matches) > 0) # ... and I expect words to be uppercased self.verify_match('CREATE', matches, text_range) # ... and I expect keywords starting in d to be skipped self.verify_miss('DELETE', matches) # and When I match with lowercase matches: List[CompletionItem] = helper.get_matches( start, text_range, True) # then I expect a lowercase result self.verify_match('create', matches, text_range) self.verify_miss('CREATE', matches)
def _prepare_edit(self, file: ScriptFile) -> TextEdit: file_line_count: int = len(file.file_lines) last_char = len(file.file_lines[file_line_count - 1]) text_range = Range.from_data(0, 0, file_line_count - 1, last_char) return TextEdit.from_data(text_range, None)
def to_range(self): """Convert the SelectionData object to a workspace service Range object""" return Range(Position(self.start_line, self.start_column), Position(self.end_line, self.end_column))