Beispiel #1
0
    def test_zend_view_hooks(self):
        test_dir = join(self.test_dir, "test_zend_view_hooks")
        content, positions = unmark_text(
            dedent("""\
            <?php if($this->values) { ?>
            <h3>You just submitted the following values:</h3>
            <ul>
              <?php foreach ($this->values as $value) :?>
              <li>
                <?= $this-><1>escape(<2>$value); ?>
              </li>
              <?php endforeach; ?>
            </ul>
            <?=
            $this->form; ?>
        """))
        phpExtraPaths = os.pathsep.join(self._ci_extra_path_dirs_)
        env = SimplePrefsEnvironment(phpExtraPaths=phpExtraPaths)

        # Test to make sure the zend hooks are only modifying the files
        # in the /view/scripts/ directories. We should *not* get the completions
        # for the above code.
        path = join(test_dir, "test_zend_view.phtml")
        writefile(path, content)
        buf = self.mgr.buf_from_path(path, lang=self.lang, env=env)
        buf.scan_if_necessary()
        self.assertCompletionsDoNotInclude2(buf, positions[1],
                                            [("function", "escape"),
                                             ("function", "render")])

        # Test to make sure the zend hooks are only modifying the ".phtml" files
        # in the /view/scripts/ directories. We should *not* get the completions
        # for the above code.
        path = join(test_dir, "test_zend_view.php")
        writefile(path, content)
        buf = self.mgr.buf_from_path(path, lang=self.lang, env=env)
        buf.scan_if_necessary()
        self.assertCompletionsDoNotInclude2(buf, positions[1],
                                            [("function", "escape"),
                                             ("function", "render")])

        # Now make sure we do get the completions on /view/scripts/ directories.
        path = join(test_dir, "views", "scripts", "test_zend_view.phtml")
        writefile(path, content)
        buf = self.mgr.buf_from_path(path, lang=self.lang, env=env)
        buf.scan_if_necessary()
        self.assertCompletionsInclude2(buf, positions[1],
                                       [("function", "escape"),
                                        ("function", "render")])
        # Make sure we do not get completions on the added "(render)" function.
        self.assertCompletionsDoNotInclude2(buf, positions[1], [
            ("function", "(render)"),
        ])
Beispiel #2
0
    def test_completions_dtd(self):
        sample = dedent("""
            <html>
                <body>
                    <<|>
                </body>
            </html>""")
        env = SimplePrefsEnvironment()
        buf, data = self._get_buf_and_data(sample, self.lang, env=env)

        # Test HTML4...
        env.set_pref("defaultHTMLDecl", "-//W3C//DTD HTML 4.01//EN")
        self.assertCompletionsInclude2(buf,
                                       data["pos"], [("element", "script")],
                                       unload=False)
        self.assertCompletionsDoNotInclude2(buf,
                                            data["pos"],
                                            [("element", "section")],
                                            unload=False)
        # Flip to HTML5...
        env.set_pref("defaultHTMLDecl", "-//W3C//DTD HTML 5//EN")
        self.assertCompletionsInclude2(buf,
                                       data["pos"], [("element", "script")],
                                       unload=False)
        self.assertCompletionsInclude2(buf,
                                       data["pos"], [("element", "section")],
                                       unload=False)
        # And back to HTML4 again
        env.set_pref("defaultHTMLDecl", "-//W3C//DTD HTML 4.01//EN")
        self.assertCompletionsInclude2(buf,
                                       data["pos"], [("element", "script")],
                                       unload=False)
        self.assertCompletionsDoNotInclude2(buf,
                                            data["pos"],
                                            [("element", "section")],
                                            unload=False)
    def setUp(self):
        if _xpcom_:
            # The tests are run outside of Komodo. If run with PyXPCOM up
            # parts codeintel will try to use the nsIDirectoryService and
            # will query dirs only provided by nsXREDirProvider -- which
            # isn't registered outside of Komodo (XRE_main() isn't called).
            # The KoTestService provides a backup.
            koTestSvc = components.classes["@activestate.com/koTestService;1"] \
                .getService(components.interfaces.koITestService)
            koTestSvc.init()

        if self._ci_test_setup_mgr_:
            env = None
            if self._ci_env_prefs_ is not None:
                env = SimplePrefsEnvironment(**self._ci_env_prefs_)
            self.mgr = Manager(
                extra_module_dirs=self._ci_extra_module_dirs_,
                db_base_dir=self._ci_db_base_dir_ or test_db_base_dir,
                db_catalog_dirs=self._ci_db_catalog_dirs_,
                db_import_everything_langs=self._ci_db_import_everything_langs,
                env=env)
            self.mgr.upgrade()
            self.mgr.initialize()
Beispiel #4
0
    def test_completions_dtd(self):
        sample = dedent("""
            <html>
                <body>
                    <<|>
                </body>
            </html>""")
        env = SimplePrefsEnvironment()
        buf, data = self._get_buf_and_data(sample, self.lang, env=env)

        # Test HTML4...
        env.set_pref("defaultHTMLDecl", "-//W3C//DTD HTML 4.01//EN")
        self.assertCompletionsInclude2(buf, data["pos"], [("element", "script")], unload=False)
        self.assertCompletionsDoNotInclude2(buf, data["pos"], [("element", "section")], unload=False)
        # Flip to HTML5...
        env.set_pref("defaultHTMLDecl", "-//W3C//DTD HTML 5//EN")
        self.assertCompletionsInclude2(buf, data["pos"], [("element", "script")], unload=False)
        self.assertCompletionsInclude2(buf, data["pos"], [("element", "section")], unload=False)
        # And back to HTML4 again
        env.set_pref("defaultHTMLDecl", "-//W3C//DTD HTML 4.01//EN")
        self.assertCompletionsInclude2(buf, data["pos"], [("element", "script")], unload=False)
        self.assertCompletionsDoNotInclude2(buf, data["pos"], [("element", "section")], unload=False)
Beispiel #5
0
    def ci_setUpClass(cls):
        if _xpcom_:
            # The tests are run outside of Komodo. If run with PyXPCOM up
            # parts codeintel will try to use the nsIDirectoryService and
            # will query dirs only provided by nsXREDirProvider -- which
            # isn't registered outside of Komodo (XRE_main() isn't called).
            # The KoTestService provides a backup.
            koTestSvc = components.classes["@activestate.com/koTestService;1"] \
                .getService(components.interfaces.koITestService)
            koTestSvc.init()

        if cls._ci_test_setup_mgr_:
            env = None
            if cls._ci_env_prefs_ is not None:
                env = SimplePrefsEnvironment(**cls._ci_env_prefs_)

            def get_extra_module_dirs():
                spec = join(dirname(
                    __file__), "..", "..", "udl", "skel", "*", "pylib")
                for d in glob(spec):
                    if glob(join(spec, "lang_*.py")):
                        yield d

                for d in cls._ci_extra_module_dirs_ or []:
                    yield d

            cls.mgr = Manager(
                extra_module_dirs=get_extra_module_dirs(),
                db_base_dir=cls._ci_db_base_dir_ or test_db_base_dir,
                db_catalog_dirs=cls._ci_db_catalog_dirs_,
                db_import_everything_langs=cls._ci_db_import_everything_langs,
                env=env)
            cls.mgr.upgrade()
            cls.mgr.initialize()

        init_xml_catalogs()
    def _codeintel_scan():
        global despair, despaired
        env = None
        mtime = None
        catalogs = []
        now = time.time()

        mgr = codeintel_manager(folders_id)
        mgr.db.event_reporter = lambda m: logger(view, 'event', m)

        try:
            env = _ci_envs_[vid]
            if env._folders != folders:
                raise KeyError
            if now > env._time:
                mtime = max(tryGetMTime(env._config_file), tryGetMTime(env._config_default_file))
                if env._mtime < mtime:
                    raise KeyError
        except KeyError:
            if env is not None:
                config_default_file = env._config_default_file
                project_dir = env._project_dir
                project_base_dir = env._project_base_dir
                config_file = env._config_file
            else:
                config_default_file = os.path.join(CODEINTEL_HOME_DIR, 'config')
                if not (config_default_file and os.path.exists(config_default_file)):
                    config_default_file = None
                project_dir = None
                project_base_dir = None
                for folder_path in folders + [path]:
                    if folder_path:
                        # Try to find a suitable project directory (or best guess):
                        for folder in ['.codeintel', '.git', '.hg', '.svn', 'trunk']:
                            project_dir = find_back(folder_path, folder)
                            if project_dir:
                                if folder == '.codeintel':
                                    if project_dir == CODEINTEL_HOME_DIR or os.path.exists(os.path.join(project_dir, 'databases')):
                                        continue
                                if folder.startswith('.'):
                                    project_base_dir = os.path.abspath(os.path.join(project_dir, '..'))
                                else:
                                    project_base_dir = project_dir
                                break
                        if project_base_dir:
                            break
                if not (project_dir and os.path.exists(project_dir)):
                    project_dir = None
                config_file = project_dir and folder == '.codeintel' and os.path.join(project_dir, 'config')
                if not (config_file and os.path.exists(config_file)):
                    config_file = None

            valid = True
            if not mgr.is_citadel_lang(lang) and not mgr.is_cpln_lang(lang):
                if lang in ('Console', 'Plain text'):
                    msg = "Invalid language: %s. Available: %s" % (lang, ', '.join(set(mgr.get_citadel_langs() + mgr.get_cpln_langs())))
                    log.debug(msg)
                    codeintel_log.warning(msg)
                valid = False

            codeintel_config_lang = codeintel_config.get(lang, {})
            codeintel_max_recursive_dir_depth = codeintel_config_lang.get('codeintel_max_recursive_dir_depth', _codeintel_max_recursive_dir_depth)
            codeintel_scan_files_in_project = codeintel_config_lang.get('codeintel_scan_files_in_project', _codeintel_scan_files_in_project)
            codeintel_selected_catalogs = codeintel_config_lang.get('codeintel_selected_catalogs', _codeintel_selected_catalogs)

            avail_catalogs = mgr.db.get_catalogs_zone().avail_catalogs()

            # Load configuration files:
            all_catalogs = []
            for catalog in avail_catalogs:
                all_catalogs.append("%s (for %s: %s)" % (catalog['name'], catalog['lang'], catalog['description']))
                if catalog['lang'] == lang:
                    if catalog['name'] in codeintel_selected_catalogs:
                        catalogs.append(catalog['name'])
            msg = "Avaliable catalogs: %s" % ', '.join(all_catalogs) or None
            log.debug(msg)
            codeintel_log.debug(msg)

            config = {
                'codeintel_max_recursive_dir_depth': codeintel_max_recursive_dir_depth,
                'codeintel_scan_files_in_project': codeintel_scan_files_in_project,
                'codeintel_selected_catalogs': catalogs,
            }
            config.update(codeintel_config_lang)

            _config = {}
            try:
                tryReadDict(config_default_file, _config)
            except Exception as e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            try:
                tryReadDict(config_file, _config)
            except Exception as e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            config.update(_config.get(lang, {}))

            for conf in ['pythonExtraPaths', 'rubyExtraPaths', 'perlExtraPaths', 'javascriptExtraPaths', 'phpExtraPaths']:
                v = [p.strip() for p in config.get(conf, []) + folders if p.strip()]
                config[conf] = os.pathsep.join(set(p if p.startswith('/') else os.path.expanduser(p) if p.startswith('~') else os.path.abspath(os.path.join(project_base_dir, p)) if project_base_dir else p for p in v if p.strip()))
            for conf, p in config.items():
                if isinstance(p, basestring) and p.startswith('~'):
                    config[conf] = os.path.expanduser(p)

            # Setup environment variables
            env = config.get('env', {})
            _environ = dict(os.environ)
            for k, v in env.items():
                _old = None
                while '$' in v and v != _old:
                    _old = v
                    v = os.path.expandvars(v)
                _environ[k] = v
            config['env'] = _environ

            env = SimplePrefsEnvironment(**config)
            env._valid = valid
            env._mtime = mtime or max(tryGetMTime(config_file), tryGetMTime(config_default_file))
            env._folders = folders
            env._config_default_file = config_default_file
            env._project_dir = project_dir
            env._project_base_dir = project_base_dir
            env._config_file = config_file
            env.__class__.get_proj_base_dir = lambda self: project_base_dir
            _ci_envs_[vid] = env
        env._time = now + 5  # don't check again in less than five seconds

        msgs = []
        if env._valid:
            if forms:
                calltip(view, 'tip', "")
                calltip(view, 'event', "")
                msg = "CodeIntel(%s) for %s@%s [%s]" % (', '.join(forms), path, pos, lang)
                msgs.append(('info', "\n%s\n%s" % (msg, "-" * len(msg))))

            if catalogs:
                msg = "New env with catalogs for '%s': %s" % (lang, ', '.join(catalogs) or None)
                log.debug(msg)
                codeintel_log.warning(msg)
                msgs.append(('info', msg))

            buf = mgr.buf_from_content(content.encode('utf-8'), lang, env, path or "<Unsaved>", 'utf-8')

            now = datetime.datetime.now()
            if not _ci_next_scan_.get(vid) or now > _ci_next_scan_[vid]:
                _ci_next_scan_[vid] = now + datetime.timedelta(seconds=10)
                if isinstance(buf, CitadelBuffer):
                    despair = 0
                    despaired = False
                    msg = "Updating indexes for '%s'... The first time this can take a while." % lang
                    print(msg, file=condeintel_log_file)
                    logger(view, 'info', msg, timeout=20000, delay=1000)
                    if not path or is_scratch:
                        buf.scan()  # FIXME: Always scanning unsaved files (since many tabs can have unsaved files, or find other path as ID)
                    else:
                        if is_dirty:
                            mtime = 1
                        else:
                            mtime = os.stat(path)[stat.ST_MTIME]
                        buf.scan(mtime=mtime, skip_scan_time_check=is_dirty)
        else:
            buf = None
        if callback:
            msg = "Doing CodeIntel for '%s' (hold on)..." % lang
            print(msg, file=condeintel_log_file)
            logger(view, 'info', msg, timeout=20000, delay=1000)
            callback(buf, msgs)
        else:
            logger(view, 'info', "")
def codeintel_scan(view,
                   path,
                   content,
                   lang,
                   callback=None,
                   pos=None,
                   forms=None):
    global despair
    for thread in threading.enumerate():
        if thread.isAlive() and thread.name == "scanning thread":
            logger(
                view,
                'info',
                "Updating indexes... The first time this can take a while. Do not despair!",
                timeout=20000,
                delay=despair)
            despair = 0
            return
    logger(view, 'info', "processing `%s': please wait..." % lang)
    is_scratch = view.is_scratch()
    is_dirty = view.is_dirty()
    id = view.id()
    folders = getattr(view.window(), 'folders', lambda: [])(
    )  # FIXME: it's like this for backward compatibility (<= 2060)

    def _codeintel_scan():
        global despair, despaired
        env = None
        mtime = None
        catalogs = []
        now = time.time()

        mgr = codeintel_manager()
        mgr.db.event_reporter = lambda m: logger(view, 'event', m)

        try:
            env = _ci_envs_[id]
            if env._folders != folders:
                raise KeyError
            if now > env._time:
                mtime = max(tryGetMTime(env._config_file),
                            tryGetMTime(env._config_default_file))
                if env._mtime < mtime:
                    raise KeyError
        except KeyError:
            if env is not None:
                config_default_file = env._config_default_file
                project_dir = env._project_dir
                project_base_dir = env._project_base_dir
                config_file = env._config_file
            else:
                config_default_file = os.path.join(CODEINTEL_HOME_DIR,
                                                   'config')
                if not (config_default_file
                        and os.path.exists(config_default_file)):
                    config_default_file = None
                project_dir = None
                project_base_dir = None
                if path:
                    # Try to find a suitable project directory (or best guess):
                    for folder in [
                            '.codeintel', '.git', '.hg', '.svn', 'trunk'
                    ]:
                        project_dir = find_folder(path, folder)
                        if project_dir:
                            if folder == '.codeintel':
                                if project_dir == CODEINTEL_HOME_DIR or os.path.exists(
                                        os.path.join(project_dir, 'db')):
                                    continue
                            if folder.startswith('.'):
                                project_base_dir = os.path.abspath(
                                    os.path.join(project_dir, '..'))
                            else:
                                project_base_dir = project_dir
                            break
                if not (project_dir and os.path.exists(project_dir)):
                    project_dir = None
                config_file = project_dir and folder == '.codeintel' and os.path.join(
                    project_dir, 'config')
                if not (config_file and os.path.exists(config_file)):
                    config_file = None

            valid = True
            if not mgr.is_citadel_lang(lang) and not mgr.is_cpln_lang(lang):
                msg = "Invalid language: %s. Available: %s" % (lang, ', '.join(
                    set(mgr.get_citadel_langs() + mgr.get_cpln_langs())))
                log.debug(msg)
                codeintel_log.warning(msg)
                valid = False

            # Load configuration files:
            for catalog in mgr.db.get_catalogs_zone().avail_catalogs():
                if catalog['lang'] == lang:
                    catalogs.append(catalog['name'])
            config = {
                "codeintel_selected_catalogs": catalogs,
                "codeintel_max_recursive_dir_depth": 10,
                "codeintel_scan_files_in_project": True,
            }

            _config = {}
            try:
                tryReadDict(config_default_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (
                    config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            try:
                tryReadDict(config_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (
                    config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            config.update(_config.get(lang, {}))
            for conf in [
                    'pythonExtraPaths', 'rubyExtraPaths', 'perlExtraPaths',
                    'javascriptExtraPaths', 'phpExtraPaths'
            ]:
                v = [
                    p.strip() for p in config.get(conf, []) + folders
                    if p.strip()
                ]
                config[conf] = os.pathsep.join(
                    set(p if p.startswith('/') else os.path.expanduser(p) if p.
                        startswith('~') else os.path.
                        abspath(os.path.join(project_base_dir, p)
                                ) if project_base_dir else p for p in v
                        if p.strip()))

            env = SimplePrefsEnvironment(**config)
            env._valid = valid
            env._mtime = mtime or max(tryGetMTime(config_file),
                                      tryGetMTime(config_default_file))
            env._folders = folders
            env._config_default_file = config_default_file
            env._project_dir = project_dir
            env._project_base_dir = project_base_dir
            env._config_file = config_file
            env.__class__.get_proj_base_dir = lambda self: project_base_dir
            _ci_envs_[id] = env
def codeintel_scan(view, path, content, lang, callback=None, pos=None, forms=None):
    global despair
    for thread in threading.enumerate():
        if thread.isAlive() and thread.name == "scanning thread":
            logger(
                view,
                "info",
                "Updating indexes... The first time this can take a while. Do not despair!",
                timeout=20000,
                delay=despair,
            )
            despair = 0
            return
    logger(view, "info", "processing `%s': please wait..." % lang)
    is_scratch = view.is_scratch()
    is_dirty = view.is_dirty()
    id = view.id()
    folders = getattr(
        view.window(), "folders", lambda: []
    )()  # FIXME: it's like this for backward compatibility (<= 2060)

    def _codeintel_scan():
        global despair, despaired
        env = None
        mtime = None
        catalogs = []
        now = time.time()

        mgr = codeintel_manager()
        mgr.db.event_reporter = lambda m: logger(view, "event", m)

        try:
            env = _ci_envs_[id]
            if env._folders != folders:
                raise KeyError
            if now > env._time:
                mtime = max(tryGetMTime(env._config_file), tryGetMTime(env._config_default_file))
                if env._mtime < mtime:
                    raise KeyError
        except KeyError:
            if env is not None:
                config_default_file = env._config_default_file
                project_dir = env._project_dir
                project_base_dir = env._project_base_dir
                config_file = env._config_file
            else:
                config_default_file = os.path.join(CODEINTEL_HOME_DIR, "config")
                if not (config_default_file and os.path.exists(config_default_file)):
                    config_default_file = None
                project_dir = None
                project_base_dir = None
                if path:
                    # Try to find a suitable project directory (or best guess):
                    for folder in [".codeintel", ".git", ".hg", ".svn", "trunk"]:
                        project_dir = find_folder(path, folder)
                        if project_dir:
                            if folder == ".codeintel":
                                if project_dir == CODEINTEL_HOME_DIR or os.path.exists(os.path.join(project_dir, "db")):
                                    continue
                            if folder.startswith("."):
                                project_base_dir = os.path.abspath(os.path.join(project_dir, ".."))
                            else:
                                project_base_dir = project_dir
                            break
                if not (project_dir and os.path.exists(project_dir)):
                    project_dir = None
                config_file = project_dir and folder == ".codeintel" and os.path.join(project_dir, "config")
                if not (config_file and os.path.exists(config_file)):
                    config_file = None

            valid = True
            if not mgr.is_citadel_lang(lang) and not mgr.is_cpln_lang(lang):
                msg = "Invalid language: %s. Available: %s" % (
                    lang,
                    ", ".join(set(mgr.get_citadel_langs() + mgr.get_cpln_langs())),
                )
                log.debug(msg)
                codeintel_log.warning(msg)
                valid = False

            # Load configuration files:
            for catalog in mgr.db.get_catalogs_zone().avail_catalogs():
                if catalog["lang"] == lang:
                    catalogs.append(catalog["name"])
            config = {
                "codeintel_selected_catalogs": catalogs,
                "codeintel_max_recursive_dir_depth": 10,
                "codeintel_scan_files_in_project": True,
            }

            _config = {}
            try:
                tryReadDict(config_default_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            try:
                tryReadDict(config_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            config.update(_config.get(lang, {}))
            for conf in [
                "pythonExtraPaths",
                "rubyExtraPaths",
                "perlExtraPaths",
                "javascriptExtraPaths",
                "phpExtraPaths",
            ]:
                v = [p.strip() for p in config.get(conf, []) + folders if p.strip()]
                config[conf] = os.pathsep.join(
                    set(
                        p
                        if p.startswith("/")
                        else os.path.expanduser(p)
                        if p.startswith("~")
                        else os.path.abspath(os.path.join(project_base_dir, p))
                        if project_base_dir
                        else p
                        for p in v
                        if p.strip()
                    )
                )

            env = SimplePrefsEnvironment(**config)
            env._valid = valid
            env._mtime = mtime or max(tryGetMTime(config_file), tryGetMTime(config_default_file))
            env._folders = folders
            env._config_default_file = config_default_file
            env._project_dir = project_dir
            env._project_base_dir = project_base_dir
            env._config_file = config_file
            env.__class__.get_proj_base_dir = lambda self: project_base_dir
            _ci_envs_[id] = env
def codeintel_scan(view, path, content, lang, callback=None, pos=None, forms=None):
    global despair
    for thread in threading.enumerate():
        if thread.isAlive() and thread.name == "scanning thread":
            logger(
                view,
                "info",
                "Updating indexes... The first time this can take a while. Do not despair!",
                timeout=20000,
                delay=despair,
            )
            despair = 0
            return
    try:
        lang = lang or guess_lang_from_path(path)
    except CodeIntelError:
        logger(view, "warning", "skip `%s': couldn't determine language" % path)
        return
    if lang.lower() in [l.lower() for l in view.settings().get("codeintel_disabled_languages", [])]:
        return
    is_scratch = view.is_scratch()
    is_dirty = view.is_dirty()
    folders = getattr(
        view.window(), "folders", lambda: []
    )()  # FIXME: it's like this for backward compatibility (<= 2060)

    def _codeintel_scan():
        global _ci_mgr_, despair, despaired
        env = None
        mtime = None
        catalogs = []
        now = time.time()
        try:
            env = _ci_envs_[path]
            if env._folders != folders:
                raise KeyError
            mgr = _ci_mgr_
            if now > env._time:
                mtime = max(tryGetMTime(env._config_file), tryGetMTime(env._config_default_file))
                if env._mtime < mtime:
                    raise KeyError
        except KeyError:
            if env is not None:
                config_default_file = env._config_default_file
                project_dir = env._project_dir
                project_base_dir = env._project_base_dir
                config_file = env._config_file
            else:
                config_default_file = os.path.join(CODEINTEL_HOME_DIR, "config")
                if not (config_default_file and os.path.exists(config_default_file)):
                    config_default_file = None
                project_dir = None
                project_base_dir = None
                if path:
                    # Try to find a suitable project directory (or best guess):
                    for folder in [".codeintel", ".git", ".hg", "trunk"]:
                        project_dir = find_folder(path, folder)
                        if project_dir and (
                            folder != ".codeintel" or not os.path.exists(os.path.join(project_dir, "db"))
                        ):
                            if folder.startswith("."):
                                project_base_dir = os.path.abspath(os.path.join(project_dir, ".."))
                            else:
                                project_base_dir = project_dir
                            break
                if not (project_dir and os.path.exists(project_dir)):
                    project_dir = None
                config_file = project_dir and folder == ".codeintel" and os.path.join(project_dir, "config")
                if not (config_file and os.path.exists(config_file)):
                    config_file = None
            if _ci_mgr_:
                mgr = _ci_mgr_
            else:
                for thread in threading.enumerate():
                    if thread.name == "CodeIntel Manager":
                        thread.finalize()  # this finalizes the index, citadel and the manager and waits them to end (join)
                mgr = Manager(
                    extra_module_dirs=_ci_extra_module_dirs_,
                    db_base_dir=_ci_db_base_dir_,
                    db_catalog_dirs=_ci_db_catalog_dirs_,
                    db_import_everything_langs=_ci_db_import_everything_langs,
                    db_event_reporter=lambda m: logger(view, "event", m),
                )
                mgr.upgrade()
                mgr.initialize()

                # Connect the logging file to the handler
                condeintel_log_file = os.path.join(mgr.db.base_dir, "codeintel.log")
                codeintel_log.handlers = [logging.StreamHandler(open(condeintel_log_file, "w", 1))]
                msg = "Starting logging SublimeCodeIntel rev %s (%s) on %s" % (
                    get_git_revision()[:12],
                    os.stat(__file__)[stat.ST_MTIME],
                    datetime.datetime.now().ctime(),
                )
                codeintel_log.info("%s\n%s" % (msg, "=" * len(msg)))

                _ci_mgr_ = mgr

            # Load configuration files:
            for catalog in mgr.db.get_catalogs_zone().avail_catalogs():
                if catalog["lang"] == lang:
                    catalogs.append(catalog["name"])
            config = {
                "codeintel_selected_catalogs": catalogs,
                "codeintel_max_recursive_dir_depth": 10,
                "codeintel_scan_files_in_project": True,
            }

            _config = {}
            try:
                tryReadDict(config_default_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            try:
                tryReadDict(config_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            config.update(_config.get(lang, {}))
            for conf in [
                "pythonExtraPaths",
                "rubyExtraPaths",
                "perlExtraPaths",
                "javascriptExtraPaths",
                "phpExtraPaths",
            ]:
                v = [p.strip() for p in config.get(conf, []) + folders if p.strip()]
                config[conf] = os.pathsep.join(
                    set(
                        p
                        if p.startswith("/")
                        else os.path.expanduser(p)
                        if p.startswith("~")
                        else os.path.abspath(os.path.join(project_base_dir, p))
                        if project_base_dir
                        else p
                        for p in v
                        if p.strip()
                    )
                )

            env = SimplePrefsEnvironment(**config)
            env._mtime = mtime or max(tryGetMTime(config_file), tryGetMTime(config_default_file))
            env._folders = folders
            env._config_default_file = config_default_file
            env._project_dir = project_dir
            env._project_base_dir = project_base_dir
            env._config_file = config_file
            env.__class__.get_proj_base_dir = lambda self: project_base_dir
            _ci_envs_[path] = env
Beispiel #10
0
class PureRailsTestCase(_BaseTestCase):
    lang = "Ruby"
    ext = ".rb"
    heredoc_support = True
    env = SimplePrefsEnvironment(codeintel_selected_catalogs=['rails'])

    def test_catalog(self):
        catalogs_zone = self.mgr.db.get_catalogs_zone()
        for catalog_info in catalogs_zone.avail_catalogs(["rails"]):
            if catalog_info['selected']:
                rails_info = catalog_info
                break
        else:
            self.fail("rails not found in the available catalogs")

        self.failUnlessEqual(rails_info["name"], "Rails")
        self.failUnless(rails_info["description"] is not None)

    def test_railsenv_model_basic(self):
        test_dir = join(self.test_dir, "railsapp01", "app", "models")
        main_filename = "cart1.rb"
        main_content, main_positions = \
          unmark_text(self.adjust_content(dedent("""\
            class Cart < ActiveRecord::<1>Base
                Cart.<2>acts_as_list
                def octopus
                    self.<3>insert_at(0)
                    i = 4
                    print "you owe #{i.<4>gigabyte} dollars"
                end
            end
        """)))
        main_path = join(test_dir, main_filename)
        writefile(main_path, main_content)
        main_buf = self.mgr.buf_from_path(main_path)
        targets = [
            [
                ("class", "Base"),
            ],
            [
                ("function", "acts_as_list"),
            ],
            [
                ("function", "insert_at"),
            ],
            [
                ("function", "megabyte"),
            ],
        ]
        for i in range(len(targets)):
            self.assertCompletionsInclude2(main_buf, main_positions[i + 1],
                                           targets[i])
            ## Verify we don't get false hits
            self.assertCompletionsDoNotInclude(
                markup_text(main_content, pos=main_positions[i + 1]),
                targets[i])

    @tag("global")
    def test_railsenv_model_toplevel_1(self):
        test_dir = join(self.test_dir, "railsapp01", "app", "models")
        main_filename = "cart1.rb"
        main_content, main_positions = \
          unmark_text(self.adjust_content(dedent("""\
            class Cart < ActiveRecord::<2>Base
                act<1>s_as_list
            end
        """)))
        main_path = join(test_dir, main_filename)
        writefile(main_path, main_content)
        main_buf = self.mgr.buf_from_path(main_path)
        pos_targets = [
            ("function", "acts_as_list"),
            ("function", "acts_as_tree"),
            ("function", "acts_as_nested_set"),
        ]
        neg_targets = [
            ("function", "add_child"),
        ]
        self.assertCompletionsInclude2(main_buf, main_positions[1],
                                       pos_targets)
        self.assertCompletionsDoNotInclude(
            markup_text(main_content, pos=main_positions[1]), neg_targets)

    @tag("global")
    def test_railsenv_model_toplevel_context(self):
        test_dir = join(self.test_dir, "railsapp01", "app", "models")
        main_filename = "cart1.rb"
        main_content, main_positions = \
          unmark_text(self.adjust_content(dedent("""\
            class Cart < ActiveRecord::Base
                val<1>
                def switch
                    des<2>troy
                end
            end
        """)))
        main_path = join(test_dir, main_filename)
        writefile(main_path, main_content)
        main_buf = self.mgr.buf_from_path(main_path)
        class_targets = [
            ("function", "validate"),
            ("function", "validate_find_options"),
            ("function", "validate_on_create"),
            ("function", "validate_on_update"),
            ("function", "validates_acceptance_of"),
            ("function", "validates_associated"),
            ("function", "validates_confirmation_of"),
            ("function", "validates_each"),
            ("function", "validates_exclusion_of"),
            ("function", "validates_format_of"),
            ("function", "validates_inclusion_of"),
            ("function", "validates_length_of"),
            ("function", "validates_numericality_of"),
            ("function", "validates_presence_of"),
            ("function", "validates_size_of"),
            ("function", "validates_uniqueness_of"),
        ]
        inst_targets = [
            ("function", "destroy"),
            ("function", "destroy_all"),
        ]
        self.assertCompletionsInclude2(main_buf, main_positions[1],
                                       class_targets)
        self.assertCompletionsInclude2(main_buf, main_positions[2],
                                       inst_targets)
        self.assertCompletionsDoNotInclude(
            markup_text(main_content, pos=main_positions[1]), inst_targets)
        self.assertCompletionsDoNotInclude(
            markup_text(main_content, pos=main_positions[2]), class_targets)

    def test_railsenv_controller_basic(self):
        test_dir = join(self.test_dir, "railsapp01", "app", "controllers")
        main_filename = "admin_controller.rb"
        main_content, main_positions = \
          unmark_text(self.adjust_content(dedent("""\
            class ApplicationController < ActionController::<1>Base
                ApplicationController.<2>after_filter :check_authentication, :except => [:signin]
                def signin 
                    self.<3>render(:layout, "sheep".<4>pluralize)
                end
            end
        """)))
        main_path = join(test_dir, main_filename)
        writefile(main_path, main_content)
        main_buf = self.mgr.buf_from_path(main_path)
        targets = [
            [
                ("class", "Base"),
            ],
            [
                ("function", "after_filter"),
            ],
            [
                ("function", "render"),
            ],
            [
                ("function", "pluralize"),
            ],
        ]
        for i in range(len(targets)):
            self.assertCompletionsInclude2(main_buf, main_positions[i + 1],
                                           targets[i])
            ## Verify we don't get false hits
            self.assertCompletionsDoNotInclude(
                markup_text(main_content, pos=main_positions[i + 1]),
                targets[i])

    def test_railsenv_controller_find_peer(self):
        test_dir = join(self.test_dir, "railsapp01", "app", "controllers")
        adminc_filename = "admin_controller%s" % self.ext
        adminc_content, adminc_positions = \
          unmark_text(self.adjust_content(dedent("""\
            module AppBogus
                # to force a choice at point 2
            end
            class AdminController < <1>App<2>licationController
                aft<3>er_filter :check_authentication, :except => [:signin]
                AdminController.filter_parameter_logging(<6>'a', 'b')
                def open
                    exp<4>ires_in 10.seconds
                    self.<5>redirect_to("chumley")
                end
            end
        """)))
        manifest = [
            (join(test_dir, "application.rb"),
             dedent("""\
                class ApplicationController < ActionController::Base
                    def foo
                    end
                end
             """)),
            (adminc_filename, adminc_content),
        ]
        for file, content in manifest:
            path = join(test_dir, file)
            writefile(path, content)
        adminc_buf = self.mgr.buf_from_path(join(test_dir, adminc_filename))
        targets = [
            None,  #0
            None,  #1
            [
                ("class", "ApplicationController"),  #2
                ("namespace", "AppBogus"),
            ],
            [
                ("function", "after_filter"),  #3
            ],
            [
                ("function", "expires_in"),  #4
                ("function", "expires_now")
            ],
            [
                ("function", "redirect_to"),  #5
                ("function", "session_enabled?"),
            ],
        ]

        #for i in range(2, 1 + len(targets)):
        for i in (2, 5):
            self.assertCompletionsInclude2(adminc_buf, adminc_positions[i],
                                           targets[i])
        self.assertCalltipIs2(
            adminc_buf, adminc_positions[6],
            dedent("""\
    (*filter_words, &block) {|key, value| ...}
    Replace sensitive paramater data from the request log.
    Filters paramaters that have any of the arguments as a
    substring. Looks in all subhashes of the param hash for keys
    to filter. If a block is given, each key and value of the
    paramater hash and all subhashes is passed to it, the value
    or key can be replaced using String#replace or similar
    method."""))

    @tag("bug65336", "knownfailure")
    # This test *sometimes* fails.
    # This test models how the behaviour described in bug 65336
    # is supposed to work
    def test_controller_deleting_peer(self):
        dirs1 = [self.test_dir, "bug65336", "app"]
        test_controller_dir = join(*(dirs1 + ["controllers"]))
        test_model_dir = join(*(dirs1 + ["models"]))
        adminc_filename = join(test_controller_dir, "admin_controller.rb")
        book_path = join(test_model_dir, "book.rb")
        cart_path = join(test_model_dir, "cart.rb")
        adminc_content, adminc_positions = \
          unmark_text(self.adjust_content(dedent("""\
                class ApplicationController < ActionController::Base
                    def foo
                       x = Cart<5>.<1>new
                       x.<2>add_i<6>tem()
                       y = Boo<7>k.<3>new
                       y.<4>re<8>ad()
                    end
                end
        """)))
        manifest = [
            (cart_path,
             dedent("""\
                 class Cart < ActiveRecord::Base
                   def add_item(a)
                   end
                 end
             """)),
            (book_path,
             dedent("""\
                 class Book < ActiveRecord::Base
                   def read(a)
                   end
                 end
             """)),
            (adminc_filename, adminc_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        adminc_buf = self.mgr.buf_from_path(adminc_filename)
        targets = [
            None,  #0
            [
                ("function", "new"),  #1
            ],
            [
                ("function", "add_item"),  #2
            ],
            [
                ("function", "new"),  #3
            ],
            [
                ("function", "read"),  #4
            ],
        ]
        for i in range(1, len(targets)):
            self.assertCompletionsInclude2(adminc_buf, adminc_positions[i],
                                           targets[i])
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[5],
                                ilk="class",
                                name="Cart",
                                line=1)
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[6],
                                ilk="function",
                                name="add_item",
                                line=2)
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[7],
                                ilk="class",
                                name="Book",
                                line=1)
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[8],
                                ilk="function",
                                name="read",
                                line=2)
        os.unlink(book_path)

        # Rebuild and scan the controller buffer with the book file deleted.
        adminc_content, adminc_positions = \
          unmark_text(self.adjust_content(dedent("""\
                class ApplicationController < ActionController::Base
                    def updated_funcname  # Force rescan
                       x = Cart.<1>new
                       x.<2>add_item()
                       y = Book.<3>new
                       y.<4>read()
                    end
                end
        """)))
        writefile(adminc_filename, adminc_content)
        adminc_buf = self.mgr.buf_from_path(adminc_filename)
        for i in (1, 2):
            self.assertCompletionsInclude2(adminc_buf, adminc_positions[i],
                                           targets[i])
        # These two tests fail.
        for i in (3, 4):
            self.assertCompletionsAre2(adminc_buf, adminc_positions[i], None)

    # Make sure migration files can see the models -- not too useful,
    # as the code-completion will be class-level ActiveRecord only,
    # but we need to know the model names

    @tag("bug68997")
    def test_migration_sees_model(self):
        dirs1 = [self.test_dir, "bug68997", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_path = join(test_model_dir, "book.rb")
        cart_path = join(test_model_dir, "cart.rb")

        dirs2 = [self.test_dir, "bug68997", "db", "migrate"]
        migrate_dir = join(*dirs2)
        migrate_path = join(migrate_dir, "001_create_books.rb")
        migrate_table_create_path = join(migrate_dir, "001_create_books.rb")
        migrate_add_data_path = join(migrate_dir, "002_add_data.rb")
        migrate_content, migrate_positions = \
          unmark_text(self.adjust_content(dedent("""\
                class CreateTitleData < ActiveRecord::Migration
                def self.up
                    Cart.<1>create(:no => 1, :fields => 2, :yet => 3)
                end
                def self.down
                    Cart.<2>delete_all
                end
                end
        """)))
        manifest = [
            (cart_path,
             dedent("""\
                 class Cart < ActiveRecord::Base
                   def add_item(a)
                   end
                 end
             """)),
            (book_path,
             dedent("""\
                 class Book < ActiveRecord::Base
                   def read(a)
                   end
                 end
             """)),
            (migrate_table_create_path,
             dedent("""\
                class CreateTitle < ActiveRecord::Migration
                  def self.up
                    create_table :books do |t|
                      t.column 'title', :string
                      t.column :author, :string
                      t.column "publisher", :string
                      t.column :rating, :float
                    end
                  end
                  def self.down
                  end
                end
             """)),
            (migrate_add_data_path, migrate_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        migrate_buf = self.mgr.buf_from_path(migrate_add_data_path)
        self.assertCompletionsInclude2(migrate_buf, migrate_positions[2], [
            ("function", "new"),
            ("function", "create"),
            ("function", "delete_all"),
        ])
        self.assertCompletionsInclude2(migrate_buf, migrate_positions[1], [
            ("function", "new"),
            ("function", "create"),
            ("function", "delete_all"),
        ])

    @tag("bug68997")
    def test_migration_sees_model(self):
        dirs1 = [self.test_dir, "bug68997b", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_path = join(test_model_dir, "book.rb")
        cart_path = join(test_model_dir, "cart.rb")

        dirs2 = [self.test_dir, "bug68997b", "db", "migrate"]
        migrate_dir = join(*dirs2)
        migrate_path = join(migrate_dir, "001_create_books.rb")
        migrate_table_create_path = join(migrate_dir, "001_create_books.rb")
        migrate_add_data_path = join(migrate_dir, "002_add_data.rb")
        migrate_content, migrate_positions = \
          unmark_text(self.adjust_content(dedent("""\
                class CreateTitleData < ActiveRecord::Migration
                def self.up
                    Cart.<1>create(:no => 1, :fields => 2, :yet => 3)
                end
                def self.down
                    Cart.<2>delete_all
                end
                end
        """)))
        manifest = [
            (cart_path,
             dedent("""\
                 class Cart < ActiveRecord::Base
                   def add_item(a)
                   end
                 end
             """)),
            (book_path,
             dedent("""\
                 class Book < ActiveRecord::Base
                   def read(a)
                   end
                 end
             """)),
            (migrate_table_create_path,
             dedent("""\
                class CreateTitle < ActiveRecord::Migration
                  def self.up
                    create_table :books do |t|
                      t.column 'title', :string
                      t.column :author, :string
                      t.column "publisher", :string
                      t.column :rating, :float
                    end
                  end
                  def self.down
                  end
                end
             """)),
            (migrate_add_data_path, migrate_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        migrate_buf = self.mgr.buf_from_path(migrate_add_data_path)
        self.assertCompletionsInclude2(migrate_buf, migrate_positions[2], [
            ("function", "new"),
            ("function", "create"),
            ("function", "delete_all"),
        ])
        self.assertCompletionsInclude2(migrate_buf, migrate_positions[1], [
            ("function", "new"),
            ("function", "create"),
            ("function", "delete_all"),
        ])

    @tag("bug69532", "railstests")
    def test_functional_test_sees_model(self):
        dirs1 = [self.test_dir, "bug69532", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_path = join(test_model_dir, "book.rb")
        cart_path = join(test_model_dir, "cart.rb")

        migrate_dir = join(self.test_dir, "bug69532", "db", "migrate")
        book_migrate_path = join(migrate_dir, "001_create_books.rb")
        cart_migrate_path = join(migrate_dir, "002_create_cart.rb")

        dirs2 = [self.test_dir, "bug69532", "test", "unit"]
        unit_dir = join(*dirs2)
        unit_book_path = join(unit_dir, "book_test.rb")
        unit_content, unit_positions = \
          unmark_text(self.adjust_content(dedent("""\                                                 
                require File.dirname(__FILE__) + '/../test_helper'
                
                class BookTest < Test::Unit::TestCase
                  fixtures :books
                  # Replace this with your real tests.
                  def test_make_book
                     roots = Book.<1>new
                     horse = Cart.<2>new
                     roots.<3>read
                     horse.<4>add_item
                     puts roots.<5>publisher + horse.<6>contents
                  end
                end
        """)))
        manifest = [
            (cart_path,
             dedent("""\
                 class Cart < ActiveRecord::Base
                   def add_item(a)
                   end
                 end
             """)),
            (book_path,
             dedent("""\
                 class Book < ActiveRecord::Base
                   def read(a)
                   end
                 end
             """)),
            (book_migrate_path,
             dedent("""\
                  def self.up
                    create_table :books do |t|
                      t.column 'title', :string
                      t.column :author, :string
                      t.column "publisher", :string
                      t.column :rating, :float
                    end
                    create_table :dishes do |t|
                      t.column 'year', :string
                      t.column :manufacturer, :string
                    end
                    create_table :books do |t|
                      t.column 'isbn', :string
                    end
                  end
                  def self.down
                  end
             """)),
            (cart_migrate_path,
             dedent("""\
                  def self.up
                    create_table :carts do |t|
                      t.column 'owner', :string
                      t.column :contents, :string
                      t.column "created_on", :datetime
                    end
                  end
                  def self.down
                  end
             """)),
            (unit_book_path, unit_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        unit_buf = self.mgr.buf_from_path(unit_book_path)
        self.assertCompletionsInclude2(
            unit_buf,
            unit_positions[1],
            [
                ("function", "new"),
                #("function", "create"),
                #("function", "delete_all"),
            ])
        self.assertCompletionsInclude2(
            unit_buf,
            unit_positions[2],
            [
                ("function", "new"),
                #("function", "create"),
                #("function", "delete_all"),
            ])
        self.assertCompletionsInclude2(unit_buf, unit_positions[3], [
            ("function", "read"),
        ])
        self.assertCompletionsInclude2(unit_buf, unit_positions[4], [
            ("function", "add_item"),
        ])

    @tag("bug69532", "knownfailure", "railstests")
    def test_functional_test_sees_migrations(self):
        dirs1 = [self.test_dir, "bug69532", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_path = join(test_model_dir, "book.rb")
        cart_path = join(test_model_dir, "cart.rb")

        migrate_dir = join(self.test_dir, "bug69532", "db", "migrate")
        book_migrate_path = join(migrate_dir, "001_create_books.rb")
        cart_migrate_path = join(migrate_dir, "002_create_cart.rb")

        dirs2 = [self.test_dir, "bug69532", "test", "unit"]
        unit_dir = join(*dirs2)
        unit_book_path = join(unit_dir, "book_test.rb")
        unit_content, unit_positions = \
          unmark_text(self.adjust_content(dedent("""\                                                 
                require File.dirname(__FILE__) + '/../test_helper'
                
                class BookTest < Test::Unit::TestCase
                  fixtures :books
                  # Replace this with your real tests.
                  def test_make_book
                     roots = Book.<1>new
                     horse = Cart.<2>new
                     roots.<3>read
                     horse.<4>add_item
                     puts roots.<5>publisher + horse.<6>contents
                  end
                end
        """)))
        manifest = [
            (cart_path,
             dedent("""\
                 class Cart < ActiveRecord::Base
                   def add_item(a)
                   end
                 end
             """)),
            (book_path,
             dedent("""\
                 class Book < ActiveRecord::Base
                   def read(a)
                   end
                 end
             """)),
            (book_migrate_path,
             dedent("""\
                  def self.up
                    create_table :books do |t|
                      t.column 'title', :string
                      t.column :author, :string
                      t.column "publisher", :string
                      t.column :rating, :float
                    end
                    create_table :dishes do |t|
                      t.column 'year', :string
                      t.column :manufacturer, :string
                    end
                    create_table :books do |t|
                      t.column 'isbn', :string
                    end
                  end
                  def self.down
                  end
             """)),
            (cart_migrate_path,
             dedent("""\
                  def self.up
                    create_table :carts do |t|
                      t.column 'owner', :string
                      t.column :contents, :string
                      t.column "created_on", :datetime
                    end
                  end
                  def self.down
                  end
             """)),
            (unit_book_path, unit_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        unit_buf = self.mgr.buf_from_path(unit_book_path)
        self.assertCompletionsInclude2(unit_buf, unit_positions[5], [
            ("function", "publisher"),
        ])
        self.assertCompletionsInclude2(unit_buf, unit_positions[6], [
            ("function", "contents"),
        ])

    @tag("bug65443")
    def test_model_sees_migrations(self):
        dirs1 = [self.test_dir, "bug65443", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_path = join(test_model_dir, "book.rb")

        dirs2 = [self.test_dir, "bug65443", "db", "migrate"]
        migrate_dir = join(*dirs2)
        migrate_path = join(migrate_dir, "001_create_books.rb")
        migrate_table_create_path = join(migrate_dir, "001_create_books.rb")
        migrate_add_column_path = join(migrate_dir, "002_add_book_items.rb")
        model_content, model_positions = \
          unmark_text(self.adjust_content(dedent("""\
                 class Book < ActiveRecord::Base
                   def get_title(a)
                       return self.<1>title
                   end
                 end
        """)))
        manifest = [
            (migrate_table_create_path,
             dedent("""\
                class Book < ActiveRecord::Migration
                  def self.up
                    create_table :books do |t|
                      t.column 'title', :string
                      t.column :author, :string
                      t.column "publisher", :string
                      t.column :rating, :float
                    end
                    create_table :dishes do |t|
                      t.column 'year', :string
                      t.column :manufacturer, :string
                    end
                    create_table :books do |t|
                      t.column 'isbn', :string
                    end
                  end
                  def self.down
                  end
                end
             """)),
            (migrate_add_column_path,
             dedent("""\
                class Book < ActiveRecord::Migration
                  def self.up
                    add_column :books, "typeface", :string
                    add_column :bookies, "bet", :string
                    add_column 'books', :year, :string
                  end
                  def self.down
                  end
                end
             """)),
            (book_path, model_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        model_buf = self.mgr.buf_from_path(book_path)
        self.assertCompletionsInclude2(model_buf, model_positions[1], [
            ("function", "title"),
            ("function", "author"),
            ("function", "publisher"),
            ("function", "isbn"),
            ("function", "rating"),
            ("function", "typeface"),
            ("function", "year"),
        ])
        self.assertCompletionsDoNotInclude2(model_buf, model_positions[1], [
            ("function", "bet"),
        ])

    books_and_dishes_migration = dedent("""\
        class Book < ActiveRecord::Migration
          def self.up
            create_table :dishes do |t|
              t.integer 'year'
              t.string :manufacturer
              t.timestamps
            end
            create_table :books do |t|
              t.string 'title'
              t.string :author
              t.string "publisher"
            end
            create_table :books do |t|
              t.column 'isbn', :string
            end
          end
          def self.down
          end
        end
     """)

    @tag("bug75440")
    def test_model_sees_rails2_migrations_1(self):
        dirs1 = [self.test_dir, "bug75440", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_path = join(test_model_dir, "book.rb")

        dirs2 = [self.test_dir, "bug75440", "db", "migrate"]
        migrate_dir = join(*dirs2)
        migrate_table_create_path = join(migrate_dir,
                                         "001_create_books_and_dishes.rb")
        model_content, model_positions = \
          unmark_text(self.adjust_content(dedent("""\
                 class Book < ActiveRecord::Base
                   def get_title(a)
                       return self.<1>title
                   end
                 end
        """)))
        manifest = [
            (migrate_table_create_path, self.books_and_dishes_migration),
            (book_path, model_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        model_buf = self.mgr.buf_from_path(book_path)
        self.assertCompletionsInclude2(model_buf, model_positions[1], [
            ("function", "title"),
            ("function", "author"),
            ("function", "publisher"),
            ("function", "isbn"),
        ])
        self.assertCompletionsDoNotInclude2(model_buf, model_positions[1], [
            ("function", "bet"),
            ("function", "manufacturer"),
            ("function", "created_at"),
            ("function", "updated_at"),
        ])

    @tag("bug75440")
    def test_model_sees_rails2_migrations_2(self):
        dirs1 = [self.test_dir, "bug75440b", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_path = join(test_model_dir, "dish.rb")

        dirs2 = [self.test_dir, "bug75440b", "db", "migrate"]
        migrate_dir = join(*dirs2)
        migrate_table_create_path = join(migrate_dir,
                                         "001_create_books_and_dishes.rb")
        model_content, model_positions = \
          unmark_text(self.adjust_content(dedent("""\
                 class Dish < ActiveRecord::Base
                   def get_plate(a)
                       return self.<1>year
                   end
                 end
        """)))
        manifest = [
            (migrate_table_create_path, self.books_and_dishes_migration),
            (book_path, model_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        model_buf = self.mgr.buf_from_path(book_path)
        # log.error("model_buf=%s", model_buf)
        self.assertCompletionsInclude2(model_buf, model_positions[1], [
            ("function", "year"),
            ("function", "manufacturer"),
            ("function", "created_at"),
            ("function", "updated_at"),
        ])
        self.assertCompletionsDoNotInclude2(model_buf, model_positions[1], [
            ("function", "shamroobah"),
            ("function", "author"),
            ("function", "publisher"),
            ("function", "title"),
        ])

    def test_controller_sees_migrations(self):
        dirs1 = [self.test_dir, "bug68997c", "app"]
        test_model_dir = join(*(dirs1 + ["models"]))
        book_model_path = join(test_model_dir, "book.rb")

        test_controller_dir = join(*(dirs1 + ["controllers"]))
        book_controller_path = join(test_controller_dir, "book_controller.rb")

        dirs2 = [self.test_dir, "bug68997c", "db", "migrate"]
        migrate_dir = join(*dirs2)
        migrate_path = join(migrate_dir, "001_create_books.rb")
        migrate_table_create_path = join(migrate_dir, "001_create_books.rb")
        migrate_add_column_path = join(migrate_dir, "002_add_book_items.rb")
        content, positions = \
          unmark_text(self.adjust_content(dedent("""\
                 class BookController < ApplicationController
                   def create
                    book = Book.new(params[:title])
                    book.<1>title = "splibitsh"
                    book.publisher<2> = "Rodoni"
                   end
                 end
        """)))
        manifest = [
            (migrate_table_create_path,
             dedent("""\
                class Book < ActiveRecord::Migration
                  def self.up
                    create_table :books do |t|
                      t.column 'title', :string
                      t.column :author, :string
                      t.column "publisher", :string
                      t.column :rating, :float
                    end
                    create_table :dishes do |t|
                      t.column 'year', :string
                      t.column :manufacturer, :string
                    end
                    create_table :books do |t|
                      t.column 'isbn', :string
                    end
                  end
                  def self.down
                  end
                end
             """)),
            (migrate_add_column_path,
             dedent("""\
                class Book < ActiveRecord::Migration
                  def self.up
                    add_column :books, "typeface", :string
                    add_column :bookies, "bet", :string
                    add_column 'books', :year, :string
                  end
                  def self.down
                  end
                end
             """)),
            (book_model_path,
             dedent("""\
                 class Book < ActiveRecord::Base
                 end
             """)),
            (book_controller_path, content),
        ]
        for mpath, mcontent in manifest:
            writefile(mpath, mcontent)
        buf = self.mgr.buf_from_path(book_controller_path)
        self.assertCompletionsInclude2(buf, positions[1], [
            ("function", "title"),
            ("function", "author"),
            ("function", "publisher"),
            ("function", "isbn"),
            ("function", "rating"),
            ("function", "typeface"),
            ("function", "year"),
        ])
        self.assertCompletionsDoNotInclude2(buf, positions[1], [
            ("function", "bet"),
        ])
        self.assertDefnMatches2(buf,
                                positions[2],
                                lang="Ruby",
                                line=6,
                                path=migrate_table_create_path)

    # This test *sometimes* fails.
    # This test models how the behaviour described in bug 65336
    # is supposed to work
    @tag("failsintermittently")
    def test_controller_find_peer(self):
        dirs1 = [self.test_dir, "peers", "app"]
        test_controller_dir = join(*(dirs1 + ["controllers"]))
        test_model_dir = join(*(dirs1 + ["models"]))
        adminc_filename = join(test_controller_dir, "admin_controller.rb")
        book_path = join(test_model_dir, "book.rb")
        cart_path = join(test_model_dir, "cart.rb")
        adminc_content, adminc_positions = \
          unmark_text(self.adjust_content(dedent("""\
                class ApplicationController < ActionController::Base
                    def foo
                       x = Cart<5>.<1>new
                       x.<2>add_i<6>tem()
                       y = Boo<7>k.<3>new
                       y.<4>re<8>ad()
                    end
                end
        """)))
        manifest = [
            (cart_path,
             dedent("""\
                 class Cart < ActiveRecord::Base
                   def add_item(a)
                   end
                 end
             """)),
            (book_path,
             dedent("""\
                 class Book < ActiveRecord::Base
                   def read(a)
                   end
                 end
             """)),
            (adminc_filename, adminc_content),
        ]
        for path, content in manifest:
            writefile(path, content)
        adminc_buf = self.mgr.buf_from_path(adminc_filename)
        targets = [
            None,  #0
            [
                ("function", "new"),  #1
            ],
            [
                ("function", "add_item"),  #2
            ],
            [
                ("function", "new"),  #3
            ],
            [
                ("function", "read"),  #4
            ],
        ]
        repl_path = 'models'
        fixed_cart_path = cart_path.replace('models', repl_path)
        fixed_book_path = book_path.replace('models', repl_path)
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[5],
                                ilk="class",
                                name="Cart",
                                line=1,
                                path=fixed_cart_path)
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[6],
                                ilk="function",
                                name="add_item",
                                line=2,
                                path=fixed_cart_path)
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[7],
                                ilk="class",
                                name="Book",
                                line=1,
                                path=fixed_book_path)
        self.assertDefnMatches2(adminc_buf,
                                adminc_positions[8],
                                ilk="function",
                                name="read",
                                line=2,
                                path=fixed_book_path)
def codeintel_scan(view, path, content, lang, callback=None, pos=None, forms=None):
    global despair
    for thread in threading.enumerate():
        if thread.isAlive() and thread.name == "scanning thread":
            logger(view, 'info', "Updating indexes... The first time this can take a while. Do not despair!", timeout=20000, delay=despair)
            despair = 0
            return
    logger(view, 'info', "processing `%s': please wait..." % lang)
    is_scratch = view.is_scratch()
    is_dirty = view.is_dirty()
    vid = view.id()
    folders = getattr(view.window(), 'folders', lambda: [])()  # FIXME: it's like this for backward compatibility (<= 2060)
    folders_id = str(hash(frozenset(folders)))
    view_settings = view.settings()
    codeintel_config = view_settings.get('codeintel_config', {})
    _codeintel_max_recursive_dir_depth = view_settings.get('codeintel_max_recursive_dir_depth', 10)
    _codeintel_scan_files_in_project = view_settings.get('codeintel_scan_files_in_project', True)
    _codeintel_selected_catalogs = view_settings.get('codeintel_selected_catalogs', [])

    def _codeintel_scan():
        global despair, despaired
        env = None
        mtime = None
        catalogs = []
        now = time.time()

        mgr = codeintel_manager(folders_id)
        mgr.db.event_reporter = lambda m: logger(view, 'event', m)

        try:
            env = _ci_envs_[vid]
            if env._folders != folders:
                raise KeyError
            if now > env._time:
                mtime = max(tryGetMTime(env._config_file), tryGetMTime(env._config_default_file))
                if env._mtime < mtime:
                    raise KeyError
        except KeyError:
            if env is not None:
                config_default_file = env._config_default_file
                project_dir = env._project_dir
                project_base_dir = env._project_base_dir
                config_file = env._config_file
            else:
                config_default_file = os.path.join(CODEINTEL_HOME_DIR, 'config')
                if not (config_default_file and os.path.exists(config_default_file)):
                    config_default_file = None
                project_dir = None
                project_base_dir = None
                for folder_path in folders + [path]:
                    if folder_path:
                        # Try to find a suitable project directory (or best guess):
                        for folder in ['.codeintel', '.git', '.hg', '.svn', 'trunk']:
                            project_dir = find_back(folder_path, folder)
                            if project_dir:
                                if folder == '.codeintel':
                                    if project_dir == CODEINTEL_HOME_DIR or os.path.exists(os.path.join(project_dir, 'databases')):
                                        continue
                                if folder.startswith('.'):
                                    project_base_dir = os.path.abspath(os.path.join(project_dir, '..'))
                                else:
                                    project_base_dir = project_dir
                                break
                        if project_base_dir:
                            break
                if not (project_dir and os.path.exists(project_dir)):
                    project_dir = None
                config_file = project_dir and folder == '.codeintel' and os.path.join(project_dir, 'config')
                if not (config_file and os.path.exists(config_file)):
                    config_file = None

            valid = True
            if not mgr.is_citadel_lang(lang) and not mgr.is_cpln_lang(lang):
                if lang in ('Console', 'Plain text'):
                    msg = "Invalid language: %s. Available: %s" % (lang, ', '.join(set(mgr.get_citadel_langs() + mgr.get_cpln_langs())))
                    log.debug(msg)
                    codeintel_log.warning(msg)
                valid = False

            codeintel_config_lang = codeintel_config.get(lang, {})
            codeintel_max_recursive_dir_depth = codeintel_config_lang.get('codeintel_max_recursive_dir_depth', _codeintel_max_recursive_dir_depth)
            codeintel_scan_files_in_project = codeintel_config_lang.get('codeintel_scan_files_in_project', _codeintel_scan_files_in_project)
            codeintel_selected_catalogs = codeintel_config_lang.get('codeintel_selected_catalogs', _codeintel_selected_catalogs)

            avail_catalogs = mgr.db.get_catalogs_zone().avail_catalogs()

            # Load configuration files:
            all_catalogs = []
            for catalog in avail_catalogs:
                all_catalogs.append("%s (for %s: %s)" % (catalog['name'], catalog['lang'], catalog['description']))
                if catalog['lang'] == lang:
                    if catalog['name'] in codeintel_selected_catalogs:
                        catalogs.append(catalog['name'])
            msg = "Avaliable catalogs: %s" % ', '.join(all_catalogs) or None
            log.debug(msg)
            codeintel_log.debug(msg)

            config = {
                'codeintel_max_recursive_dir_depth': codeintel_max_recursive_dir_depth,
                'codeintel_scan_files_in_project': codeintel_scan_files_in_project,
                'codeintel_selected_catalogs': catalogs,
            }
            config.update(codeintel_config_lang)

            _config = {}
            try:
                tryReadDict(config_default_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            try:
                tryReadDict(config_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            config.update(_config.get(lang, {}))

            for conf in ['pythonExtraPaths', 'rubyExtraPaths', 'perlExtraPaths', 'javascriptExtraPaths', 'phpExtraPaths']:
                v = [p.strip() for p in config.get(conf, []) + folders if p.strip()]
                config[conf] = os.pathsep.join(set(p if p.startswith('/') else os.path.expanduser(p) if p.startswith('~') else os.path.abspath(os.path.join(project_base_dir, p)) if project_base_dir else p for p in v if p.strip()))
            for conf, p in config.items():
                if isinstance(p, basestring) and p.startswith('~'):
                    config[conf] = os.path.expanduser(p)

            # Setup environment variables
            env = config.get('env', {})
            _environ = dict(os.environ)
            for k, v in env.items():
                _old = None
                while '$' in v and v != _old:
                    _old = v
                    v = os.path.expandvars(v)
                _environ[k] = v
            config['env'] = _environ

            env = SimplePrefsEnvironment(**config)
            env._valid = valid
            env._mtime = mtime or max(tryGetMTime(config_file), tryGetMTime(config_default_file))
            env._folders = folders
            env._config_default_file = config_default_file
            env._project_dir = project_dir
            env._project_base_dir = project_base_dir
            env._config_file = config_file
            env.__class__.get_proj_base_dir = lambda self: project_base_dir
            _ci_envs_[vid] = env
    def _codeintel_scan():
        global despair, despaired
        env = None
        mtime = None
        catalogs = []
        now = time.time()

        mgr = codeintel_manager(folders_id)
        mgr.db.event_reporter = lambda m: logger(view, 'event', m)

        try:
            env = _ci_envs_[vid]
            if env._folders != folders:
                raise KeyError
            if now > env._time:
                mtime = max(tryGetMTime(env._config_file), tryGetMTime(env._config_default_file))
                if env._mtime < mtime:
                    raise KeyError
        except KeyError:
            if env is not None:
                config_default_file = env._config_default_file
                project_dir = env._project_dir
                project_base_dir = env._project_base_dir
                config_file = env._config_file
            else:
                config_default_file = os.path.join(CODEINTEL_HOME_DIR, 'config')
                if not (config_default_file and os.path.exists(config_default_file)):
                    config_default_file = None
                project_dir = None
                project_base_dir = None
                for folder_path in folders + [path]:
                    if folder_path:
                        # Try to find a suitable project directory (or best guess):
                        for folder in ['.codeintel', '.git', '.hg', '.svn', 'trunk']:
                            project_dir = find_back(folder_path, folder)
                            if project_dir:
                                if folder == '.codeintel':
                                    if project_dir == CODEINTEL_HOME_DIR or os.path.exists(os.path.join(project_dir, 'databases')):
                                        continue
                                if folder.startswith('.'):
                                    project_base_dir = os.path.abspath(os.path.join(project_dir, '..'))
                                else:
                                    project_base_dir = project_dir
                                break
                        if project_base_dir:
                            break
                if not (project_dir and os.path.exists(project_dir)):
                    project_dir = None
                config_file = project_dir and folder == '.codeintel' and os.path.join(project_dir, 'config')
                if not (config_file and os.path.exists(config_file)):
                    config_file = None

            valid = True
            if not mgr.is_citadel_lang(lang) and not mgr.is_cpln_lang(lang):
                if lang in ('Console', 'Plain text'):
                    msg = "Invalid language: %s. Available: %s" % (lang, ', '.join(set(mgr.get_citadel_langs() + mgr.get_cpln_langs())))
                    log.debug(msg)
                    codeintel_log.warning(msg)
                valid = False

            codeintel_config_lang = codeintel_config.get(lang, {})
            codeintel_max_recursive_dir_depth = codeintel_config_lang.get('codeintel_max_recursive_dir_depth', _codeintel_max_recursive_dir_depth)
            codeintel_scan_files_in_project = codeintel_config_lang.get('codeintel_scan_files_in_project', _codeintel_scan_files_in_project)
            codeintel_selected_catalogs = codeintel_config_lang.get('codeintel_selected_catalogs', _codeintel_selected_catalogs)

            avail_catalogs = mgr.db.get_catalogs_zone().avail_catalogs()

            # Load configuration files:
            all_catalogs = []
            for catalog in avail_catalogs:
                all_catalogs.append("%s (for %s: %s)" % (catalog['name'], catalog['lang'], catalog['description']))
                if catalog['lang'] == lang:
                    if catalog['name'] in codeintel_selected_catalogs:
                        catalogs.append(catalog['name'])
            msg = "Avaliable catalogs: %s" % ', '.join(all_catalogs) or None
            log.debug(msg)
            codeintel_log.debug(msg)

            config = {
                'codeintel_max_recursive_dir_depth': codeintel_max_recursive_dir_depth,
                'codeintel_scan_files_in_project': codeintel_scan_files_in_project,
                'codeintel_selected_catalogs': catalogs,
            }
            config.update(codeintel_config_lang)

            _config = {}
            try:
                tryReadDict(config_default_file, _config)
            except Exception as e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            try:
                tryReadDict(config_file, _config)
            except Exception as e:
                msg = "Malformed configuration file '%s': %s" % (config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            config.update(_config.get(lang, {}))

            for conf in ['pythonExtraPaths', 'rubyExtraPaths', 'perlExtraPaths', 'javascriptExtraPaths', 'phpExtraPaths']:
                v = [p.strip() for p in config.get(conf, []) + folders if p.strip()]
                config[conf] = os.pathsep.join(set(p if p.startswith('/') else os.path.expanduser(p) if p.startswith('~') else os.path.abspath(os.path.join(project_base_dir, p)) if project_base_dir else p for p in v if p.strip()))
            for conf, p in config.items():
                if isinstance(p, basestring) and p.startswith('~'):
                    config[conf] = os.path.expanduser(p)

            # Setup environment variables
            env = config.get('env', {})
            _environ = dict(os.environ)
            for k, v in env.items():
                _old = None
                while '$' in v and v != _old:
                    _old = v
                    v = os.path.expandvars(v)
                _environ[k] = v
            config['env'] = _environ

            env = SimplePrefsEnvironment(**config)
            env._valid = valid
            env._mtime = mtime or max(tryGetMTime(config_file), tryGetMTime(config_default_file))
            env._folders = folders
            env._config_default_file = config_default_file
            env._project_dir = project_dir
            env._project_base_dir = project_base_dir
            env._config_file = config_file
            env.__class__.get_proj_base_dir = lambda self: project_base_dir
            _ci_envs_[vid] = env
        env._time = now + 5  # don't check again in less than five seconds

        msgs = []
        if env._valid:
            if forms:
                calltip(view, 'tip', "")
                calltip(view, 'event', "")
                msg = "CodeIntel(%s) for %s@%s [%s]" % (', '.join(forms), path, pos, lang)
                msgs.append(('info', "\n%s\n%s" % (msg, "-" * len(msg))))

            if catalogs:
                msg = "New env with catalogs for '%s': %s" % (lang, ', '.join(catalogs) or None)
                log.debug(msg)
                codeintel_log.warning(msg)
                msgs.append(('info', msg))

            buf = mgr.buf_from_content(content.encode('utf-8'), lang, env, path or "<Unsaved>", 'utf-8')

            now = datetime.datetime.now()
            if not _ci_next_scan_.get(vid) or now > _ci_next_scan_[vid]:
                _ci_next_scan_[vid] = now + datetime.timedelta(seconds=10)
                if isinstance(buf, CitadelBuffer):
                    despair = 0
                    despaired = False
                    msg = "Updating indexes for '%s'... The first time this can take a while." % lang
                    print(msg, file=condeintel_log_file)
                    logger(view, 'info', msg, timeout=20000, delay=1000)
                    if not path or is_scratch:
                        buf.scan()  # FIXME: Always scanning unsaved files (since many tabs can have unsaved files, or find other path as ID)
                    else:
                        if is_dirty:
                            mtime = 1
                        else:
                            mtime = os.stat(path)[stat.ST_MTIME]
                        buf.scan(mtime=mtime, skip_scan_time_check=is_dirty)
        else:
            buf = None
        if callback:
            msg = "Doing CodeIntel for '%s' (hold on)..." % lang
            print(msg, file=condeintel_log_file)
            logger(view, 'info', msg, timeout=20000, delay=1000)
            callback(buf, msgs)
        else:
            logger(view, 'info', "")
S_RESULT = '[result]\n'
TIMEOUT = 4000

_fn = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'path.ini')
if os.path.isfile(_fn):
    with open(_fn, 'r') as f:
        paths = f.readline()
print 'Paths:\n' + '\n'.join(paths.split(os.pathsep))

d = {}
d['phpExtraPaths'] = paths
d['pythonExtraPaths'] = paths
d['perlExtraPaths'] = paths
d['javascriptExtraPaths'] = paths
d['rubyExtraPaths'] = paths
env = SimplePrefsEnvironment(**d)

mgr = Manager()
mgr.upgrade()
mgr.initialize()


#------------------------
def file_text(fn):
    try:
        with open(fn, 'r') as f:
            text = f.readlines()
        return ''.join(text)
    except IOError as e:
        if e.errno == errno.ENOENT:  # No such file or directory
            logging.error(e.strerror + ': "' + fn + '"')
def codeintel_scan(view,
                   path,
                   content,
                   lang,
                   callback=None,
                   pos=None,
                   forms=None):
    global despair
    for thread in threading.enumerate():
        if thread.isAlive() and thread.name == "scanning thread":
            logger(
                view,
                'info',
                "Updating indexes... The first time this can take a while. Do not despair!",
                timeout=20000,
                delay=despair)
            despair = 0
            return
    try:
        lang = lang or guess_lang_from_path(path)
    except CodeIntelError:
        logger(view, 'warning',
               "skip `%s': couldn't determine language" % path)
        return
    if lang.lower() in [
            l.lower()
            for l in view.settings().get('codeintel_disabled_languages', [])
    ]:
        return
    is_scratch = view.is_scratch()
    is_dirty = view.is_dirty()
    folders = getattr(view.window(), 'folders', lambda: [])(
    )  # FIXME: it's like this for backward compatibility (<= 2060)

    def _codeintel_scan():
        global _ci_mgr_, despair, despaired
        env = None
        mtime = None
        catalogs = []
        now = time.time()
        try:
            env = _ci_envs_[path]
            if env._folders != folders:
                raise KeyError
            mgr = _ci_mgr_
            if now > env._time:
                mtime = max(tryGetMTime(env._config_file),
                            tryGetMTime(env._config_default_file))
                if env._mtime < mtime:
                    raise KeyError
        except KeyError:
            if env is not None:
                config_default_file = env._config_default_file
                project_dir = env._project_dir
                project_base_dir = env._project_base_dir
                config_file = env._config_file
            else:
                config_default_file = os.path.join(CODEINTEL_HOME_DIR,
                                                   'config')
                if not (config_default_file
                        and os.path.exists(config_default_file)):
                    config_default_file = None
                project_dir = None
                project_base_dir = None
                if path:
                    # Try to find a suitable project directory (or best guess):
                    for folder in ['.codeintel', '.git', '.hg', 'trunk']:
                        project_dir = find_folder(path, folder)
                        if project_dir and (
                                folder != '.codeintel' or not os.path.exists(
                                    os.path.join(project_dir, 'db'))):
                            if folder.startswith('.'):
                                project_base_dir = os.path.abspath(
                                    os.path.join(project_dir, '..'))
                            else:
                                project_base_dir = project_dir
                            break
                if not (project_dir and os.path.exists(project_dir)):
                    project_dir = None
                config_file = project_dir and folder == '.codeintel' and os.path.join(
                    project_dir, 'config')
                if not (config_file and os.path.exists(config_file)):
                    config_file = None
            if _ci_mgr_:
                mgr = _ci_mgr_
            else:
                for thread in threading.enumerate():
                    if thread.name == "CodeIntel Manager":
                        thread.finalize(
                        )  # this finalizes the index, citadel and the manager and waits them to end (join)
                mgr = Manager(
                    extra_module_dirs=_ci_extra_module_dirs_,
                    db_base_dir=_ci_db_base_dir_,
                    db_catalog_dirs=_ci_db_catalog_dirs_,
                    db_import_everything_langs=_ci_db_import_everything_langs,
                    db_event_reporter=lambda m: logger(view, 'event', m),
                )
                mgr.upgrade()
                mgr.initialize()

                # Connect the logging file to the handler
                condeintel_log_file = os.path.join(mgr.db.base_dir,
                                                   'codeintel.log')
                codeintel_log.handlers = [
                    logging.StreamHandler(open(condeintel_log_file, 'w', 1))
                ]
                msg = "Starting logging SublimeCodeIntel rev %s (%s) on %s" % (
                    get_git_revision()[:12], os.stat(__file__)[stat.ST_MTIME],
                    datetime.datetime.now().ctime())
                codeintel_log.info("%s\n%s" % (msg, "=" * len(msg)))

                _ci_mgr_ = mgr

            # Load configuration files:
            for catalog in mgr.db.get_catalogs_zone().avail_catalogs():
                if catalog['lang'] == lang:
                    catalogs.append(catalog['name'])
            config = {
                "codeintel_selected_catalogs": catalogs,
                "codeintel_max_recursive_dir_depth": 10,
                "codeintel_scan_files_in_project": True,
            }

            _config = {}
            try:
                tryReadDict(config_default_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (
                    config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            try:
                tryReadDict(config_file, _config)
            except Exception, e:
                msg = "Malformed configuration file '%s': %s" % (
                    config_default_file, e)
                log.error(msg)
                codeintel_log.error(msg)
            config.update(_config.get(lang, {}))
            for conf in [
                    'pythonExtraPaths', 'rubyExtraPaths', 'perlExtraPaths',
                    'javascriptExtraPaths', 'phpExtraPaths'
            ]:
                v = [
                    p.strip() for p in config.get(conf, []) + folders
                    if p.strip()
                ]
                config[conf] = os.pathsep.join(
                    set(p if p.startswith('/') else os.path.expanduser(p) if p.
                        startswith('~') else os.path.
                        abspath(os.path.join(project_base_dir, p)
                                ) if project_base_dir else p for p in v
                        if p.strip()))

            env = SimplePrefsEnvironment(**config)
            env._mtime = mtime or max(tryGetMTime(config_file),
                                      tryGetMTime(config_default_file))
            env._folders = folders
            env._config_default_file = config_default_file
            env._project_dir = project_dir
            env._project_base_dir = project_base_dir
            env._config_file = config_file
            env.__class__.get_proj_base_dir = lambda self: project_base_dir
            _ci_envs_[path] = env