def testCanIncreaseId(self): rm = RequestIdManager() _id1 = rm.new_id(self.view, None) _id2 = rm.new_id(self.view, None) _id3 = rm.new_id(self.view, None) self.assertEqual(_id1, '0') self.assertEqual(_id2, '1') self.assertEqual(_id3, '2')
def __init__(self): self.roots = [] self.priority_files = [] self.requests = RequestsQueue('requests') self.responses = AnalyzerQueue('responses') self.request_ids = RequestIdManager()
class AnalysisServer(object): MAX_ID = 9999999 _request_id_lock = threading.Lock() _op_lock = threading.Lock() _write_lock = threading.Lock() _request_id = -1 server = None def __init__(self): self.roots = [] self.priority_files = [] self.requests = RequestsQueue('requests') self.responses = AnalyzerQueue('responses') self.request_ids = RequestIdManager() @property def stdout(self): return AnalysisServer.server.proc.stdout @property def stdin(self): return AnalysisServer.server.proc.stdin def get_request_id(self, view, response_type): return self.request_ids.new_id(view, response_type) @staticmethod def ping(): try: return AnalysisServer.server.is_running except AttributeError: return def start_handlers(self): reqh = RequestHandler(self) reqh.daemon = True reqh.start() resh = ResponseHandler(self) resh.daemon = True resh.start() @property def proc(self): return AnalysisServer.server def add_root(self, view, path): """ Adds `path` to the monitored roots if it is unknown. If a `pubspec.yaml` is found in the path, its parent is monitored. Otherwise the passed-in directory name is monitored. @path Can be a directory or a file path. """ if not path: _logger.debug('not a valid path: %s', path) return new_root_path = find_pubspec_path(path) if not new_root_path: # It seems we're not in a pub package, so we're probably looking # at a loose .dart file. new_root_path = os.path.dirname(path) _logger.debug('did not find pubspec.yaml in path: %s', path) _logger.debug('set root to: %s', new_root_path) with AnalysisServer._op_lock: if new_root_path not in self.roots: _logger.debug('adding new root: %s', new_root_path) self.roots.append(new_root_path) self.send_set_roots(view, self.roots) return _logger.debug('root already known: %s', new_root_path) def start(self): if AnalysisServer.ping(): _logger.info('AnalysisServer is already running') return self.send_get_version() sdk = SDK() _logger.info('starting AnalysisServer') AnalysisServer.server = PipeServer([ sdk.path_to_dart, sdk.path_to_analysis_snapshot, '--sdk={0}'.format(sdk.path), '--file-read-mode normalize-eol-always', ]) def do_start(): try: AnalysisServer.server.start(working_dir=sdk.path) self.start_handlers() self.start_stdout_watcher() except Exception as e: _logger.error('could not start server properly') _logger.error(e) return threading.Thread(target=do_start).start() def start_stdout_watcher(self): sdk = SDK() t = StdoutWatcher(self, sdk.path) # Thread dies with the main thread. t.daemon = True t.start() def stop(self): req = requests.shut_down(str(AnalysisServer.MAX_ID + 100)) self.requests.put(req, priority=TaskPriority.HIGHEST, block=False) self.requests.put({'_internal': _SIGNAL_STOP}, block=False) self.responses.put({'_internal': _SIGNAL_STOP}, block=False) # self.server.stop() def write(self, data): with AnalysisServer._write_lock: data = (json.dumps(data) + '\n').encode('utf-8') _logger.debug('writing to stdin: %s', data) self.stdin.write(data) self.stdin.flush() def send_set_roots(self, view, included=[], excluded=[]): included = [f for f in included if not self.should_ignore_file(f)] if not (included or excluded): return req = AnalysisSetAnalysisRootsParams(included, excluded) _logger.info('sending set_roots request') self.requests.put(req.to_request( self.get_request_id(view, AnalysisSetAnalysisRootsResult)), block=False) def send_get_version(self, view=None): view = get_active_view() req = ServerGetVersionParams().to_request( self.get_request_id(view, ServerGetVersionResult)) _logger.info('sending get version request') self.requests.put(req, block=False) def send_add_content(self, view): if self.should_ignore_file(view.file_name()): return content = view.substr(sublime.Region(0, view.size())) req = AnalysisUpdateContentParams( {view.file_name(): AddContentOverlay(content)}) _logger.info('sending update content request - add') # track this type of req as it may expire # TODO: when this file is saved, we must remove the overlays. self.requests.put(req.to_request( self.get_request_id(view, AnalysisUpdateContentResult)), view=view, priority=TaskPriority.HIGH, block=False) def send_remove_content(self, view): if self.should_ignore_file(view.file_name()): return req = AnalysisUpdateContentParams( {view.file_name(): RemoveContentOverlay()}) _logger.info('sending update content request - delete') self.requests.put(req.to_request( self.get_request_id(view, AnalysisUpdateContentResult)), view=view, priority=TaskPriority.HIGH, block=False) def send_set_priority_files(self, view, files): if files == self.priority_files: return definite_files = [f for f in files if not self.should_ignore_file(f)] if not definite_files: return req = AnalysisSetPriorityFilesParams(definite_files) self.requests.put(req.to_request( self.get_request_id(view, AnalysisSetPriorityFilesResult)), priority=TaskPriority.HIGH, block=False) req2 = AnalysisSetSubscriptionsParams( {AnalysisService.NAVIGATION: definite_files}) self.requests.put(req2.to_request( self.get_request_id(view, ServerSetSubscriptionsResult)), priority=TaskPriority.HIGH, block=False) def send_get_suggestions(self, view, file, offset): new_id = self.get_request_id(view, CompletionGetSuggestionsResult) with editor_context.autocomplete_context as actx: actx.invalidate() actx.request_id = new_id req = CompletionGetSuggestionsParams(file, offset) req = req.to_request(new_id) self.requests.put(req, priority=TaskPriority.HIGH, block=False) def send_format_file(self, view): new_id = self.get_request_id(view, EditFormatResult) if not view.file_name(): _logger.info( "aborting sending request for formatting - no file name") return r0 = None try: r0 = view.sel()[0] except IndexError: r0 = sublime.Region(0) # TODO: Implement lineLength parameter. req = EditFormatParams(view.file_name(), r0.begin(), r0.size()) req = req.to_request(new_id) _logger.info("now sending request for formatting") self.requests.put(req, priority=TaskPriority.HIGH, block=False) def should_ignore_file(self, path): project = DartProject.from_path(path) is_a_third_party_file = (project and is_path_under( project.path_to_packages, path)) if is_a_third_party_file: return True sdk = SDK() return is_path_under(sdk.path, path)
def testValidateCanFail(self): rm = RequestIdManager() _id = rm.new_id(self.view, int) self.assertFalse(rm.validate(self.view, {'id': '1'}))
def testValidationCanSucceed(self): rm = RequestIdManager() _id = rm.new_id(self.view, int) self.assertTrue(rm.validate(self.view, {'id': '0'}))
def testCanRetrieveResponseType(self): rm = RequestIdManager() _id = rm.new_id(self.view, int) self.assertEqual(rm.get_response_type(self.view, '0'), int)
def testIdsWrapAround(self): rm = RequestIdManager() rm._id = 1 << 10 _id = rm.new_id(self.view, None) self.assertEqual(_id, '0')
class AnalysisServer(object): MAX_ID = 9999999 _request_id_lock = threading.Lock() _op_lock = threading.Lock() _write_lock = threading.Lock() _request_id = -1 server = None def __init__(self): self.roots = [] self.priority_files = [] self.requests = RequestsQueue('requests') self.responses = AnalyzerQueue('responses') self.request_ids = RequestIdManager() @property def stdout(self): return AnalysisServer.server.proc.stdout @property def stdin(self): return AnalysisServer.server.proc.stdin def get_request_id(self, view, response_type): return self.request_ids.new_id(view, response_type) @staticmethod def ping(): try: return AnalysisServer.server.is_running except AttributeError: return def start_handlers(self): reqh = RequestHandler(self) reqh.daemon = True reqh.start() resh = ResponseHandler(self) resh.daemon = True resh.start() @property def proc(self): return AnalysisServer.server def add_root(self, view, path): """ Adds `path` to the monitored roots if it is unknown. If a `pubspec.yaml` is found in the path, its parent is monitored. Otherwise the passed-in directory name is monitored. @path Can be a directory or a file path. """ if not path: _logger.debug('not a valid path: %s', path) return new_root_path = find_pubspec_path(path) if not new_root_path: # It seems we're not in a pub package, so we're probably looking # at a loose .dart file. new_root_path = os.path.dirname(path) _logger.debug('did not find pubspec.yaml in path: %s', path) _logger.debug('set root to: %s', new_root_path) with AnalysisServer._op_lock: if new_root_path not in self.roots: _logger.debug('adding new root: %s', new_root_path) self.roots.append(new_root_path) self.send_set_roots(view, self.roots) return _logger.debug('root already known: %s', new_root_path) def start(self): if AnalysisServer.ping(): _logger.info('AnalysisServer is already running') return self.send_get_version() sdk = SDK() _logger.info('starting AnalysisServer') AnalysisServer.server = PipeServer([sdk.path_to_dart, sdk.path_to_analysis_snapshot, '--sdk={0}'.format(sdk.path), '--file-read-mode normalize-eol-always', ]) def do_start(): try: AnalysisServer.server.start(working_dir=sdk.path) self.start_handlers() self.start_stdout_watcher() except Exception as e: _logger.error('could not start server properly') _logger.error(e) return threading.Thread(target=do_start).start() def start_stdout_watcher(self): sdk = SDK() t = StdoutWatcher(self, sdk.path) # Thread dies with the main thread. t.daemon = True t.start() def stop(self): req = requests.shut_down(str(AnalysisServer.MAX_ID + 100)) self.requests.put(req, priority=TaskPriority.HIGHEST, block=False) self.requests.put({'_internal': _SIGNAL_STOP}, block=False) self.responses.put({'_internal': _SIGNAL_STOP}, block=False) # self.server.stop() def write(self, data): with AnalysisServer._write_lock: data = (json.dumps(data) + '\n').encode('utf-8') _logger.debug('writing to stdin: %s', data) self.stdin.write(data) self.stdin.flush() def send_set_roots(self, view, included=[], excluded=[]): included = [f for f in included if not self.should_ignore_file(f)] if not (included or excluded): return req = AnalysisSetAnalysisRootsParams(included, excluded) _logger.info('sending set_roots request') self.requests.put(req.to_request(self.get_request_id(view, AnalysisSetAnalysisRootsResult)), block=False) def send_get_version(self, view=None): view = get_active_view() req = ServerGetVersionParams().to_request( self.get_request_id(view, ServerGetVersionResult)) _logger.info('sending get version request') self.requests.put(req, block=False) def send_add_content(self, view): if self.should_ignore_file(view.file_name()): return content = view.substr(sublime.Region(0, view.size())) req = AnalysisUpdateContentParams({view.file_name(): AddContentOverlay(content)}) _logger.info('sending update content request - add') # track this type of req as it may expire # TODO: when this file is saved, we must remove the overlays. self.requests.put(req.to_request(self.get_request_id(view, AnalysisUpdateContentResult)), view=view, priority=TaskPriority.HIGH, block=False) def send_remove_content(self, view): if self.should_ignore_file(view.file_name()): return req = AnalysisUpdateContentParams({view.file_name(): RemoveContentOverlay()}) _logger.info('sending update content request - delete') self.requests.put(req.to_request(self.get_request_id(view, AnalysisUpdateContentResult)), view=view, priority=TaskPriority.HIGH, block=False) def send_set_priority_files(self, view, files): if files == self.priority_files: return definite_files = [f for f in files if not self.should_ignore_file(f)] if not definite_files: return req = AnalysisSetPriorityFilesParams(definite_files) self.requests.put(req.to_request(self.get_request_id(view, AnalysisSetPriorityFilesResult)), priority=TaskPriority.HIGH, block=False) req2 = AnalysisSetSubscriptionsParams({AnalysisService.NAVIGATION: definite_files}) self.requests.put(req2.to_request(self.get_request_id(view, ServerSetSubscriptionsResult)), priority=TaskPriority.HIGH, block=False) def send_get_suggestions(self, view, file, offset): new_id = self.get_request_id(view, CompletionGetSuggestionsResult) with editor_context.autocomplete_context as actx: actx.invalidate() actx.request_id = new_id req = CompletionGetSuggestionsParams(file, offset) req = req.to_request(new_id) self.requests.put(req, priority=TaskPriority.HIGH, block=False) def send_format_file(self, view): new_id = self.get_request_id(view, EditFormatResult) if not view.file_name(): _logger.info("aborting sending request for formatting - no file name") return r0 = None try: r0 = view.sel()[0] except IndexError: r0 = sublime.Region(0) # TODO: Implement lineLength parameter. req = EditFormatParams(view.file_name(), r0.begin(), r0.size()) req = req.to_request(new_id) _logger.info("now sending request for formatting") self.requests.put(req, priority=TaskPriority.HIGH, block=False) def should_ignore_file(self, path): project = DartProject.from_path(path) is_a_third_party_file = (project and is_path_under(project.path_to_packages, path)) if is_a_third_party_file: return True sdk = SDK() return is_path_under(sdk.path, path)