def __init__(self):
        self.roots = []
        self.priority_files = []
        self.requests = AnalyzerQueue('requests')
        self.responses = AnalyzerQueue('responses')

        reqh = RequestHandler(self)
        reqh.daemon = True
        reqh.start()

        resh = ResponseHandler(self)
        resh.daemon = True
        resh.start()
Exemple #2
0
    def __init__(self):
        self.roots = []
        self.priority_files = []
        self.requests = AnalyzerQueue('requests')
        self.responses = AnalyzerQueue('responses')

        reqh = RequestHandler(self)
        reqh.daemon = True
        reqh.start()

        resh = ResponseHandler(self)
        resh.daemon = True
        resh.start()
class AnalysisServer(object):
    MAX_ID = 9999999
    # Halts all worker threads until the server is ready.
    _ready_barrier = threading.Barrier(4, timeout=5)

    _request_id_lock = threading.Lock()
    _op_lock = threading.Lock()
    _write_lock = threading.Lock()

    _request_id = -1

    server = None

    @property
    def ready_barrier(self):
        return AnalysisServer._ready_barrier

    @property
    def stdout(self):
        return AnalysisServer.server.proc.stdout

    @property
    def stdin(self):
        return AnalysisServer.server.proc.stdin

    @staticmethod
    def get_request_id():
        with AnalysisServer._request_id_lock:
            if AnalysisServer._request_id >= AnalysisServer.MAX_ID:
                AnalysisServer._request_id = -1
            AnalysisServer._request_id += 1
            return str(AnalysisServer._request_id)

    @staticmethod
    def ping():
        try:
            return AnalysisServer.server.is_running
        except AttributeError:
            return

    def __init__(self):
        self.roots = []
        self.priority_files = []
        self.requests = RequestsQueue('requests')
        self.responses = AnalyzerQueue('responses')

        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, 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(self.roots)
                return

        _logger.debug('root already known: %s', new_root_path)

    def start(self):
        if AnalysisServer.ping():
            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)])
        AnalysisServer.server.start(working_dir=sdk.path)

        self.start_stdout_watcher()

        try:
            # Server is ready.
            self.ready_barrier.wait()
        except threading.BrokenBarrierError:
            _logger.error('could not start server properly')
            return

    def start_stdout_watcher(self):
        sdk = SDK()
        t = StdoutWatcher(self, sdk.path)
        # Thread dies with the main thread.
        t.daemon = True
        # XXX: This is necessary. If we call t.start() directly, ST hangs.
        sublime.set_timeout_async(t.start, 0)

    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, included=[], excluded=[]):
        req = AnalysisSetAnalysisRootsParams(included, excluded)
        _logger.info('sending set_roots request')
        self.requests.put(req.to_request(self.get_request_id()), block=False)

    def send_get_version(self):
        req = ServerGetVersionParams().to_request(self.get_request_id())
        _logger.info('sending get version request')
        self.requests.put(req, block=False)

    def send_add_content(self, view):
        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=view,
                          priority=TaskPriority.HIGH,
                          block=False)

    def send_remove_content(self, view):
        req = AnalysisUpdateContentParams({view.file_name(): RemoveContentOverlay()})
        _logger.info('sending update content request - delete')
        self.requests.put(req.to_request(self.get_request_id()),
                view=view,
                priority=TaskPriority.HIGH,
                block=False)

    def send_set_priority_files(self, files):
        if files == self.priority_files:
            return


        req = AnalysisSetPriorityFilesParams(files)
        self.requests.put(req.to_request(self.get_request_id()),
                priority=TaskPriority.HIGH, block=False)

        req2 = AnalysisSetSubscriptionsParams({AnalysisService.NAVIGATION: files})
        self.requests.put(req2.to_request(self.get_request_id()),
                priority=TaskPriority.HIGH, block=False)
Exemple #4
0
class AnalysisServer(object):
    MAX_ID = 9999999
    # Halts all worker threads until the server is ready.
    _ready_barrier = threading.Barrier(4, timeout=5)

    _request_id_lock = threading.Lock()
    _op_lock = threading.Lock()
    _write_lock = threading.Lock()

    _request_id = -1

    server = None

    @property
    def ready_barrier(self):
        return AnalysisServer._ready_barrier

    @property
    def stdout(self):
        return AnalysisServer.server.proc.stdout

    @property
    def stdin(self):
        return AnalysisServer.server.proc.stdin

    @staticmethod
    def get_request_id():
        with AnalysisServer._request_id_lock:
            if AnalysisServer._request_id >= AnalysisServer.MAX_ID:
                AnalysisServer._request_id = -1
            AnalysisServer._request_id += 1
            return AnalysisServer._request_id

    @staticmethod
    def ping():
        try:
            return AnalysisServer.server.is_running
        except AttributeError:
            return

    def __init__(self):
        self.roots = []
        self.priority_files = []
        self.requests = AnalyzerQueue('requests')
        self.responses = AnalyzerQueue('responses')

        reqh = RequestHandler(self)
        reqh.daemon = True
        reqh.start()

        resh = ResponseHandler(self)
        resh.daemon = True
        resh.start()

    @property
    def proc(self):
        return AnalysisServer.server

    def new_token(self):
        w = sublime.active_window()
        v = w.active_view()
        now = datetime.now()
        # 'c' indicates that this id was created at the client-side.
        token = w.id(), v.id(), '{}:{}:c{}'.format(
            w.id(), v.id(), AnalysisServer.get_request_id())
        return token

    def add_root(self, 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

        p = find_pubspec_path(path)
        if not p:
            _logger.debug('did not found pubspec.yaml in path: %s', path)
            return

        with AnalysisServer._op_lock:
            if p not in self.roots:
                _logger.debug('adding new root: %s', p)
                self.roots.append(p)
                self.send_set_roots(self.roots)
                return

        _logger.debug('root already known: %s', p)

    def start(self):
        if AnalysisServer.ping():
            return

        sdk = SDK()

        _logger.info('starting AnalysisServer')

        AnalysisServer.server = PipeServer([
            'dart', sdk.path_to_analysis_snapshot, '--sdk={0}'.format(sdk.path)
        ])
        AnalysisServer.server.start(working_dir=sdk.path)

        self.start_stdout_watcher()

        try:
            # Server is ready.
            self.ready_barrier.wait()
        except threading.BrokenBarrierError:
            _logger.error('could not start server properly')
            return

    def start_stdout_watcher(self):
        sdk = SDK()
        t = StdoutWatcher(self, sdk.path)
        # Thread dies with the main thread.
        t.daemon = True
        # XXX: This is necessary. If we call t.start() directly, ST hangs.
        sublime.set_timeout_async(t.start, 0)

    def stop(self):
        req = requests.shut_down(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, included=[], excluded=[]):
        _, _, token = self.new_token()
        req = requests.set_roots(token, included, excluded)
        _logger.info('sending set_roots request')
        self.requests.put(req, block=False)

    def send_find_top_level_decls(self, view, pattern):
        w_id, v_id, token = self.new_token()
        req = requests.find_top_level_decls(token, pattern)
        _logger.info('sending top level decls request')
        # TODO(guillermooo): Abstract this out.
        # track this type of req as it may expire
        g_req_to_resp['search']["{}:{}".format(w_id, v_id)] = token
        g_editor_context.search_id = token
        self.requests.put(req,
                          view=view,
                          priority=TaskPriority.HIGHEST,
                          block=False)

    def send_find_element_refs(self, view, potential=False):
        if not view:
            return

        _, _, token = self.new_token()
        fname = view.file_name()
        offset = view.sel()[0].b
        req = requests.find_element_refs(token, fname, offset, potential)
        _logger.info('sending find_element_refs request')
        g_editor_context.search_id = token
        self.requests.put(req,
                          view=view,
                          priority=TaskPriority.HIGHEST,
                          block=False)

    def send_add_content(self, view):
        w_id, v_id, token = self.new_token()
        data = {
            'type': 'add',
            'content': view.substr(sublime.Region(0, view.size()))
        }
        files = {view.file_name(): data}
        req = requests.update_content(token, files)
        _logger.info('sending update content request - add')
        # track this type of req as it may expire
        self.requests.put(req,
                          view=view,
                          priority=TaskPriority.HIGH,
                          block=False)

    def send_remove_content(self, view):
        w_id, v_id, token = self.new_token()
        files = {view.file_name(): {"type": "remove"}}
        req = requests.update_content(token, files)
        _logger.info('sending update content request - delete')
        self.requests.put(req,
                          view=view,
                          priority=TaskPriority.HIGH,
                          block=False)

    def send_set_priority_files(self, files):
        if files == self.priority_files:
            return

        w_id, v_id, token = self.new_token()
        _logger.info('sending set priority files request')
        req = requests.set_priority_files(token, files)
        self.requests.put(req, priority=TaskPriority.HIGH, block=False)
class AnalysisServer(object):
    MAX_ID = 9999999
    # Halts all worker threads until the server is ready.
    _ready_barrier = threading.Barrier(4, timeout=5)

    _request_id_lock = threading.Lock()
    _op_lock = threading.Lock()
    _write_lock = threading.Lock()

    _request_id = -1

    server = None

    @property
    def ready_barrier(self):
        return AnalysisServer._ready_barrier

    @property
    def stdout(self):
        return AnalysisServer.server.proc.stdout

    @property
    def stdin(self):
        return AnalysisServer.server.proc.stdin

    @staticmethod
    def get_request_id():
        with AnalysisServer._request_id_lock:
            if AnalysisServer._request_id >= AnalysisServer.MAX_ID:
                AnalysisServer._request_id = -1
            AnalysisServer._request_id += 1
            return AnalysisServer._request_id

    @staticmethod
    def ping():
        try:
            return AnalysisServer.server.is_running
        except AttributeError:
            return

    def __init__(self):
        self.roots = []
        self.priority_files = []
        self.requests = AnalyzerQueue('requests')
        self.responses = AnalyzerQueue('responses')

        reqh = RequestHandler(self)
        reqh.daemon = True
        reqh.start()

        resh = ResponseHandler(self)
        resh.daemon = True
        resh.start()

    @property
    def proc(self):
        return AnalysisServer.server

    def new_token(self):
        w = sublime.active_window()
        v = w.active_view()
        now = datetime.now()
        # 'c' indicates that this id was created at the client-side.
        token = w.id(), v.id(), '{}:{}:c{}'.format(w.id(), v.id(),
                                  AnalysisServer.get_request_id())
        return token

    def add_root(self, 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

        p = find_pubspec_path(path)
        if not p:
            _logger.debug('did not found pubspec.yaml in path: %s', path)
            return

        with AnalysisServer._op_lock:
            if p not in self.roots:
                _logger.debug('adding new root: %s', p)
                self.roots.append(p)
                self.send_set_roots(self.roots)
                return

        _logger.debug('root already known: %s', p)

    def start(self):
        if AnalysisServer.ping():
            return

        sdk = SDK()

        _logger.info('starting AnalysisServer')

        AnalysisServer.server = PipeServer(['dart',
                            sdk.path_to_analysis_snapshot,
                           '--sdk={0}'.format(sdk.path)])
        AnalysisServer.server.start(working_dir=sdk.path)

        self.start_stdout_watcher()

        try:
            # Server is ready.
            self.ready_barrier.wait()
        except threading.BrokenBarrierError:
            _logger.error('could not start server properly')
            return

    def start_stdout_watcher(self):
        sdk = SDK()
        t = StdoutWatcher(self, sdk.path)
        # Thread dies with the main thread.
        t.daemon = True
        # XXX: This is necessary. If we call t.start() directly, ST hangs.
        sublime.set_timeout_async(t.start, 0)

    def stop(self):
        req = requests.shut_down(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, included=[], excluded=[]):
        _, _, token = self.new_token()
        req = requests.set_roots(token, included, excluded)
        _logger.info('sending set_roots request')
        self.requests.put(req, block=False)

    def send_find_top_level_decls(self, view, pattern):
        w_id, v_id, token = self.new_token()
        req = requests.find_top_level_decls(token, pattern)
        _logger.info('sending top level decls request')
        # TODO(guillermooo): Abstract this out.
        # track this type of req as it may expire
        g_req_to_resp['search']["{}:{}".format(w_id, v_id)] = token
        g_editor_context.search_id = token
        self.requests.put(req,
                          view=view,
                          priority=TaskPriority.HIGHEST,
                          block=False)

    def send_find_element_refs(self, view, potential=False):
        if not view:
            return

        _, _, token = self.new_token()
        fname = view.file_name()
        offset = view.sel()[0].b
        req = requests.find_element_refs(token, fname, offset, potential)
        _logger.info('sending find_element_refs request')
        g_editor_context.search_id = token
        self.requests.put(req, view=view, priority=TaskPriority.HIGHEST,
                          block=False)

    def send_add_content(self, view):
        w_id, v_id, token = self.new_token()
        data = {'type': 'add', 'content': view.substr(sublime.Region(0,
                                                            view.size()))}
        files = {view.file_name(): data}
        req = requests.update_content(token, files)
        _logger.info('sending update content request - add')
        # track this type of req as it may expire
        self.requests.put(req, view=view, priority=TaskPriority.HIGH,
                          block=False)

    def send_remove_content(self, view):
        w_id, v_id, token = self.new_token()
        files = {view.file_name(): {"type": "remove"}}
        req = requests.update_content(token, files)
        _logger.info('sending update content request - delete')
        self.requests.put(req, view=view, priority=TaskPriority.HIGH,
                          block=False)

    def send_set_priority_files(self, files):
        if files == self.priority_files:
            return

        w_id, v_id, token = self.new_token()
        _logger.info('sending set priority files request')
        req = requests.set_priority_files(token, files)
        self.requests.put(req, priority=TaskPriority.HIGH, block=False)
Exemple #6
0
 def __init__(self):
     self.roots = []
     self.priority_files = []
     self.requests = RequestsQueue('requests')
     self.responses = AnalyzerQueue('responses')
     self.request_ids = RequestIdManager()
Exemple #7
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)
 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)
Exemple #10
0
class AnalysisServer(object):
    MAX_ID = 9999999
    # Halts all worker threads until the server is ready.
    _ready_barrier = threading.Barrier(4, timeout=5)

    _request_id_lock = threading.Lock()
    _op_lock = threading.Lock()
    _write_lock = threading.Lock()

    _request_id = -1

    server = None

    @property
    def ready_barrier(self):
        return AnalysisServer._ready_barrier

    @property
    def stdout(self):
        return AnalysisServer.server.proc.stdout

    @property
    def stdin(self):
        return AnalysisServer.server.proc.stdin

    @staticmethod
    def get_request_id():
        with AnalysisServer._request_id_lock:
            if AnalysisServer._request_id >= AnalysisServer.MAX_ID:
                AnalysisServer._request_id = -1
            AnalysisServer._request_id += 1
            return str(AnalysisServer._request_id)

    @staticmethod
    def ping():
        try:
            return AnalysisServer.server.is_running
        except AttributeError:
            return

    def __init__(self):
        self.roots = []
        self.priority_files = []
        self.requests = RequestsQueue('requests')
        self.responses = AnalyzerQueue('responses')

        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, 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(self.roots)
                return

        _logger.debug('root already known: %s', new_root_path)

    def start(self):
        if AnalysisServer.ping():
            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)])
        AnalysisServer.server.start(working_dir=sdk.path)

        self.start_stdout_watcher()

        try:
            # Server is ready.
            self.ready_barrier.wait()
        except threading.BrokenBarrierError:
            _logger.error('could not start server properly')
            return

    def start_stdout_watcher(self):
        sdk = SDK()
        t = StdoutWatcher(self, sdk.path)
        # Thread dies with the main thread.
        t.daemon = True
        # XXX: This is necessary. If we call t.start() directly, ST hangs.
        sublime.set_timeout_async(t.start, 0)

    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, 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()), block=False)

    def send_get_version(self):
        req = ServerGetVersionParams().to_request(self.get_request_id())
        _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=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=view,
                priority=TaskPriority.HIGH,
                block=False)

    def send_set_priority_files(self, 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()),
                priority=TaskPriority.HIGH, block=False)

        req2 = AnalysisSetSubscriptionsParams({AnalysisService.NAVIGATION: definite_files})
        self.requests.put(req2.to_request(self.get_request_id()),
                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)