Exemple #1
0
	def do_finish(self, can_switch_to_pdf):
		self.output_view.run_command("do_finish_edit")

		if _HAS_PHANTOMS and self.show_errors_inline:
			self.create_errs_by_file()
			self.update_phantoms()

		# can_switch_to_pdf indicates a pdf should've been created
		if can_switch_to_pdf:
			# if using output_directory, follow the copy_output_on_build setting
			# files are copied to the same directory as the main tex file
			if self.output_directory is not None:
				copy_on_build = get_setting('copy_output_on_build', True)
				if copy_on_build is None or copy_on_build is True:
					shutil.copy2(
						os.path.join(
							self.output_directory,
							self.tex_base + u'.pdf'
						),
						os.path.dirname(self.file_name)
					)
				elif isinstance(copy_on_build, list):
					for ext in copy_on_build:
						shutil.copy2(
							os.path.join(
								self.output_directory,
								self.tex_base + ext
							),
							os.path.dirname(self.file_name)
						)

			if get_setting('open_pdf_on_build', True):
				self.view.run_command("jump_to_pdf", {"from_keybinding": False})
Exemple #2
0
    def _create_formatted_entries(self, bib_entries):
        # create the formatted entries
        autocomplete_format = get_setting("cite_autocomplete_format")
        panel_format = get_setting("cite_panel_format")

        meta_data = frozendict(
            cache_time=long(time.time()),
            version=_VERSION,
            autocomplete_format=autocomplete_format,
            panel_format=panel_format
        )

        formatted_entries = tuple(
            frozendict(**{
                "keyword": entry["keyword"],
                "<prefix_match>": bibformat.create_prefix_match_str(entry),
                "<panel_formatted>": tuple(
                    bibformat.format_entry(s, entry) for s in panel_format
                ),
                "<autocomplete_formatted>":
                    bibformat.format_entry(autocomplete_format, entry)
            })
            for entry in bib_entries
        )

        return meta_data, formatted_entries
Exemple #3
0
def _create_formatted_entries(formatted_cache_name, bib_entries, cache_time):
    # create the formatted entries
    autocomplete_format = get_setting("cite_autocomplete_format")
    panel_format = get_setting("cite_panel_format")

    meta_data = {
        "cache_time": cache_time,
        "version": _VERSION,
        "autocomplete_format": autocomplete_format,
        "panel_format": panel_format
    }
    formatted_entries = [
        {
            "keyword": entry["keyword"],
            "<prefix_match>": bibformat.create_prefix_match_str(entry),
            "<panel_formatted>": [
                bibformat.format_entry(s, entry) for s in panel_format
            ],
            "<autocomplete_formatted>":
                bibformat.format_entry(autocomplete_format, entry)
        }
        for entry in bib_entries
    ]

    cache.write_global(formatted_cache_name, (meta_data, formatted_entries))
    return formatted_entries
Exemple #4
0
    def _run_with_sumatra_exe(self, commands):
        def _no_binary():
            message = (
                'Could not find SumatraPDF.exe. '
                'Please ensure the "sumatra" setting in your '
                'LaTeXTools settings is set and points to the location '
                'of Sumatra on your computer.'
            )

            def _error_msg():
                sublime.error_message(message)

            sublime.set_timeout(_error_msg, 1)
            print(message)

        # paranoia
        if not isinstance(commands, list):
            commands = [commands]

        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = 4  # SHOWNOACTIVE

        # favour 'sumatra' setting under viewer_settings if
        # it exists, otherwise, use the platform setting
        sumatra_binary = get_setting('viewer_settings', {}).\
            get('sumatra', get_setting('windows', {}).
                get('sumatra', 'SumatraPDF.exe'))

        if sumatra_binary == '' or sumatra_binary is None:
            sumatra_binary = self._find_sumatra_exe()

        if sumatra_binary is None:
            _no_binary()
            return

        try:
            subprocess.Popen(
                [sumatra_binary] + commands,
                startupinfo=startupinfo
            )
        except OSError:
            exc_info = sys.exc_info()

            sumatra_exe = self._find_sumatra_exe()
            if sumatra_exe is not None and sumatra_exe != sumatra_binary:
                try:
                    subprocess.Popen(
                        [sumatra_exe] + commands,
                        startupinfo=startupinfo
                    )
                except OSError:
                    traceback.print_exc()
                    _no_binary()
                    return
            else:
                traceback.print_exception(*exc_info)
                _no_binary()
                return
def plugin_loaded():
    # get additional entries from the settings
    _setting_entries = get_setting("fillall_helper_entries", [])
    _filter_invalid_entries(_setting_entries)
    _fillall_entries.extend(_setting_entries)

    _fillall_entries.extend([
        {
            "regex": r'(?:edulcni|tupni)\\',
            "extensions": [e[1:] for e in get_tex_extensions()],
            "strip_extensions": [".tex"]
        },
        {
            "regex": r'(?:\][^{}\[\]]*\[)?scihpargedulcni\\',
            "extensions": get_setting("image_types", [
                "pdf", "png", "jpeg", "jpg", "eps"
            ])
        },
        {
            "regex": r'(?:\][^{}\[\]]*\[)?ecruoserbibdda\\',
            "extensions": ["bib"]
        },
        {
            "regex": r'yhpargoilbib\\',
            "extensions": ["bib"],
            "strip_extensions": [".bib"],
            "comma_separated": True
        }
    ])

    # update the fields of the entries
    _update_input_entries(_fillall_entries)

    _fillall_entries.extend([
        {
            "regex": r'([^{}\[\]]*)\{(?:\][^{}\[\]]*\[)?ssalctnemucod\\',
            "type": "cached",
            "cache_name": "cls"
        },
        {
            "regex": r'([^{}\[\]]*)\{(?:\][^{}\[\]]*\[)?egakcapesu\\',
            "type": "cached",
            "cache_name": "pkg"
        },
        {
            "regex": r'([^{}\[\]]*)\{elytsyhpargoilbib\\',
            "type": "cached",
            "cache_name": "bst"
        }
    ])

    global _TEX_INPUT_GROUP_MAPPING, TEX_INPUT_FILE_REGEX
    _TEX_INPUT_GROUP_MAPPING = dict((i, v) for i, v in enumerate(_fillall_entries))
    TEX_INPUT_FILE_REGEX = re.compile(
        "(?:{0})".format("|".join(entry["regex"] for entry in _fillall_entries))
    )
	def run(self):
		# Retrieve root file and dirname.
		view = self.window.active_view()

		root_file = getTeXRoot.get_tex_root(view)
		if root_file is None:
			sublime.status_message('Could not find TEX root. Please ensure that either you have configured a TEX root in your project settings or have a LaTeX document open.')
			print('Could not find TEX root. Please ensure that either you have configured a TEX root in your project settings or have a LaTeX document open.')
			return

		if not os.path.isfile(root_file):
			message = "Could not find TEX root {0}.".format(root_file)
			sublime.status_message(message)
			print(message)
			return

		# clear the cache
		try:
			cache.delete_local_cache(root_file)
		except:
			print('Error while trying to delete local cache')
			traceback.print_exc()

		path = os.path.dirname(root_file)

		# Load the files to delete from the settings
		temp_files_exts = get_setting('temp_files_exts',
			['.blg', '.bbl', '.aux', '.log', '.brf', '.nlo', '.out', '.dvi',
			 '.ps', '.lof', '.toc', '.fls', '.fdb_latexmk', '.pdfsync',
			 '.synctex.gz', '.ind', '.ilg', '.idx'])

		ignored_folders = get_setting('temp_files_ignored_folders',
			['.git', '.svn', '.hg'])
		ignored_folders = set(ignored_folders)

		for dir_path, dir_names, file_names in os.walk(path):
			dir_names[:] = [d for d in dir_names if d not in ignored_folders]
			for file_name in file_names:
				for ext in temp_files_exts:
					if file_name.endswith(ext):
						file_name_to_del = os.path.join(dir_path, file_name)
						if os.path.exists(file_name_to_del):
							try:
								os.remove(file_name_to_del)
							except OSError:
								# basically here for locked files in Windows,
								# but who knows what we might find?
								print('Error while trying to delete {0}'.format(file_name_to_del))
								traceback.print_exc()
						# exit extension
						break

		sublime.status_message("Deleted temp files")
    def run(self, edit, insert_char=""):
        # get view and location of first selection, which we expect to be just the cursor position
        view = self.view
        point = view.sel()[0].b
        print (point)
        # Only trigger within LaTeX
        # Note using score_selector rather than match_selector
        if not view.score_selector(point,
                "text.tex.latex"):
            return


        if insert_char:
#            ed = view.begin_edit()
#            point += view.insert(ed, point, insert_char)
#            view.end_edit(ed)
            # The above was roundabout and did not work on ST3!
            point += view.insert(edit, point, insert_char)
            # Get prefs and toggles to see if we are auto-triggering
            # This is only the case if we also must insert , or {, so we don't need a separate arg
            do_ref = get_setting('ref_auto_trigger', True)
            do_cite = get_setting('cite_auto_trigger', True)
        else: # if we didn't autotrigger, we must surely run
            do_ref = True
            do_cite = True

        print (do_ref,do_cite)

        # Get the contents of the current line, from the beginning of the line to
        # the current point
        line = view.substr(sublime.Region(view.line(point).a, point))
        # print line

        # Reverse
        line = line[::-1]


        if re.match(OLD_STYLE_REF_REGEX, line) or re.match(NEW_STYLE_REF_REGEX, line):
            if do_ref:
                print ("Dispatching ref")
                view.run_command("latex_ref")
            else:
                pass # Don't do anything if we match ref completion but we turned it off
        elif re.match(OLD_STYLE_CITE_REGEX, line) or re.match(NEW_STYLE_CITE_REGEX, line):
            if do_cite:
                print ("Dispatching cite")
                view.run_command("latex_cite")
            else:
                pass # ditto for cite
        else: # here we match nothing, so error out regardless of autotrigger settings
            sublime.error_message("Ref/cite: unrecognized format.")
            return
    def on_load_async(self, view):
        if not view.score_selector(0, 'text.tex.latex'):
            return
        on_load = get_setting('cache_on_load', {}, view=view)
        if not on_load or not any(on_load.values()):
            return

        tex_root = get_tex_root(view)
        if tex_root is None:
            return

        self._TEX_CACHES[view.id()] = local_cache = LocalCache(tex_root)
        self._TEX_ROOT_REFS[tex_root] += 1

        # because cache state is shared amongst all documents sharing a tex
        # root, this ensure we only load the analysis ONCE in the on_load
        # event
        if (
            not local_cache.has('analysis') and
            on_load.get('analysis', False)
        ):
            self.run_analysis(tex_root)

        if tex_root not in self._BIB_CACHES:
            if on_load.get('bibliography', False):
                self.run_bib_cache(tex_root)

            self._BIB_CACHES[tex_root] = bib_caches = []

            LocalCache(tex_root).invalidate('bib_files')
            bib_files = find_bib_files(tex_root)

            plugins = get_setting(
                'bibliography_plugins', ['traditional'], view=view)
            if not isinstance(plugins, list):
                plugins = [plugins]

            if 'new' in plugins or 'new_bibliography' in plugins:
                for bib_file in bib_files:
                    bib_caches.append(BibCache('new', bib_file))

            if (
                'traditional' in plugins or
                'traditional_bibliography' in plugins
            ):
                for bib_file in bib_files:
                    bib_caches.append(BibCache('trad', bib_file))

        self.run_cache_update()
Exemple #9
0
    def _run_with_sumatra_exe(self, commands):
        def _no_binary():
            message = (
                'Could not find SumatraPDF.exe. '
                'Please ensure the "sumatra" setting in your '
                'LaTeXTools settings is set and points to the location '
                'of Sumatra on your computer.'
            )

            def _error_msg():
                sublime.error_message(message)

            sublime.set_timeout(_error_msg, 1)
            print(message)

        # paranoia
        if not isinstance(commands, list):
            commands = [commands]

        # favour 'sumatra' setting under viewer_settings if
        # it exists, otherwise, use the platform setting
        sumatra_binary = get_setting('viewer_settings', {}).\
            get('sumatra', get_setting('windows', {}).
                get('sumatra', 'SumatraPDF.exe')) or 'SumatraPDF.exe'

        try:
            external_command(
                [sumatra_binary] + commands,
                use_texpath=False, show_window=True
            )
        except OSError:
            exc_info = sys.exc_info()

            sumatra_exe = self._find_sumatra_exe()
            if sumatra_exe is not None and sumatra_exe != sumatra_binary:
                try:
                    external_command(
                        [sumatra_exe] + commands,
                        use_texpath=False, show_window=True
                    )
                except OSError:
                    traceback.print_exc()
                    _no_binary()
                    return
            else:
                traceback.print_exception(*exc_info)
                _no_binary()
                return
    def __init__(self, ana, only_file=None):
        # retrieve the labels and the sections
        toc_section_commands = get_setting("toc_section_commands", [])
        toc_indentations = get_setting("toc_indentations", {})
        toc_labels = get_setting("toc_labels", [])

        labels = ana.filter_commands(toc_section_commands + toc_labels)
        # filter the labels and sections to only get the labels
        # (faster than an additional query)
        secs = [c for c in labels if c.command in toc_section_commands]

        if only_file:
            labels = [l for l in labels if l.file_name == only_file]
            secs = [s for s in secs if s.file_name == only_file]

        # create the user readably captions
        # get the minimal indent (to lower the minimal section indent to 0)
        max_indent_value = max(toc_indentations.values())
        indent_offset = min([
            toc_indentations.get(com.command, max_indent_value)
            for com in secs
        ] + [0])

        caption_secs = [_make_caption(toc_indentations, s, indent_offset)
                        for s in secs]

        caption_labels = [_make_caption(toc_indentations, l, indent_offset)
                          for l in labels]

        self.__only_sec = True
        # init the superclass with a copy of the section elements
        super(show_toc_quickpanel, self).__init__(
            list(caption_secs), list(secs))

        # story necessary fields
        self.__secs = secs
        self.__labels = labels
        self.__caption_secs = caption_secs
        self.__caption_labels = caption_labels

        # add a item to show the labels
        self.add_item(quickpanel.AT_END, self.__show_string,
                      done_handler=self.__show_labels)
        # register a function to hide the labels
        self.done_handler[self.__hide_string] = self.__hide_labels

        # show the quickpanel
        self.show_quickpanel()
def find_image(tex_root, file_name, tex_file_name=None):
    ana = analysis.get_analysis(tex_root)
    base_path = ana.tex_base_path(tex_file_name)

    image_types = get_setting(
        "image_types", [
            "png", "pdf", "jpg", "jpeg", "eps"
        ])

    file_path = os.path.normpath(
        os.path.join(base_path, file_name))
    _, extension = os.path.splitext(file_path)
    extension = extension[1:]  # strip the leading point
    if not extension:
        for ext in image_types:
            test_path = file_path + "." + ext
            print("Test file: '{0}'".format(test_path))
            if os.path.exists(test_path):
                extension = ext
                file_path = test_path
                print("Found file: '{0}'".format(test_path))
                break
    if not os.path.exists(file_path):
        return None
    return file_path
    def on_post_save_async(self, view):
        if not view.score_selector(0, 'text.tex.latex'):
            return

        on_save = get_setting('cache_on_save', {}, view=view)
        if not on_save or not any(on_save.values()):
            return

        tex_root = get_tex_root(view)
        if tex_root is None:
            return

        _id = view.id()
        if _id not in self._TEX_CACHES:
            local_cache = self._TEX_CACHES[_id] = LocalCache(tex_root)
        else:
            local_cache = self._TEX_CACHES[_id]

        if on_save.get('analysis', False):
            # ensure the cache of bib_files is rebuilt on demand
            local_cache.invalidate('bib_files')
            self.run_analysis(tex_root)

        if on_save.get('bibliography', False):
            self.run_bib_cache(tex_root)

        self.run_cache_update()
def _directive_spellcheck_completions(view, value, ac=True):
    user_sc = get_setting("tex_spellcheck_paths", view=view, default={})
    locales = sorted(user_sc.keys())

    locales.extend(installed_locales)

    def get_locale(loc):
        try:
            loc = detect_spellcheck.normalize_locale(loc)
            dic = user_sc.get(loc) or detect_spellcheck.get_dict_path(loc)
            if ac:
                _, dic = os.path.split(dic)
            elif dic.startswith("Packages/"):
                dic = dic[len("Packages/"):]
        except:
            dic = "locale"
        return dic
    locales = [
        loc
        for loc in map(_prettify_locale, locales)
        if loc.startswith(value)
    ]

    if ac:
        comp = [
            ("{0}\t{1}".format(loc, get_locale(loc)), loc)
            for loc in locales
        ]
    else:
        comp = [[loc, get_locale(loc)] for loc in locales], locales
    return comp
def get_jobname(view_or_root):
    root = get_root(view_or_root)
    if root is None:
        return None

    # exit condition: texify and simple do not support jobname
    # so always return the root path
    if using_texify_or_simple():
        return os.path.splitext(
            os.path.basename(root)
        )[0]

    jobname = get_directive(view_or_root, 'jobname')
    if (jobname is None or jobname == '') and view_or_root != root:
        jobname = get_directive(root, 'jobname')

    if jobname is None or jobname == '':
        jobname = get_setting('jobname')

    if jobname is None or jobname == '':
        return os.path.splitext(
            os.path.basename(root)
        )[0]

    return jobname
    def _get_settings(self):
        '''
        returns evince-related settings as a tuple
        (python, sync_wait)
        '''
        linux_settings = get_setting('linux', {})
        # TODO python2 should eventually be deprecated
        python = linux_settings.get('python')
        if python is None or python == '':
            python = linux_settings.get('python2')

        if python is None or python == '':
            if self.PYTHON is not None:
                python = self.PYTHON
            else:
                try:
                    subprocess.check_call(['python', '-c', 'import dbus'])
                    python = 'python'
                except subprocess.CalledProcessError:
                    try:
                        subprocess.check_call(['python3', '-c', 'import dbus'])
                        python = 'python3'
                    except subprocess.CalledProcessError:
                        sublime.error_message(
                            '''Cannot find a valid Python interpreter.
                            Please set the python setting in your LaTeXTools settings.
                            '''.strip()
                        )
                        # exit the viewer process
                        raise Exception('Cannot find a valid interpreter')
                self.PYTHON = python
        return (
            python,
            linux_settings.get('sync_wait') or 1.0
        )
Exemple #16
0
def focus_st():
	sublime_command = get_sublime_exe()

	if sublime_command is not None:
		platform = sublime.platform()
		# TODO: this does not work on OSX
		# and I don't know why...
		if platform == 'osx':
			return

		plat_settings = get_setting(platform, {})
		wait_time = plat_settings.get('keep_focus_delay', 0.5)

		def keep_focus():
			startupinfo = None
			shell = False
			if platform == 'windows':
				startupinfo = subprocess.STARTUPINFO()
				startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
				shell = _ST3

			subprocess.Popen(
				sublime_command,
				startupinfo=startupinfo,
				shell=shell,
				env=os.environ
			)

		if hasattr(sublime, 'set_async_timeout'):
			sublime.set_async_timeout(keep_focus, int(wait_time * 1000))
		else:
			sublime.set_timeout(keep_focus, int(wait_time * 1000))
    def on_query_completions(self, view, prefix, locations):
        # Only trigger within LaTeX
        if not view.match_selector(locations[0],
                "text.tex.latex"):
            return []

        point = locations[0]

        try:
            completions, prefix, post_brace, new_point_a, new_point_b = get_cite_completions(view, point, autocompleting=True)
        except UnrecognizedCiteFormatError:
            return []
        except NoBibFilesError:
            sublime.status_message("No bib files found!")
            return []
        except BibParsingError as e:
            sublime.status_message("Bibliography " + e.filename + " is broken!")
            return []

        if prefix:
            completions = [comp for comp in completions if prefix.lower() in "%s %s" % (comp[0].lower(), comp[1].lower())]
            prefix += " "

        # get preferences for formating of autocomplete entries
        cite_autocomplete_format = get_setting('cite_autocomplete_format',
            "{keyword}: {title}")

        r = [(prefix + cite_autocomplete_format.format(keyword=keyword, title=title, author=author, year=year, author_short=author_short, title_short=title_short, journal=journal),
                keyword + post_brace) for (keyword, title, author, year, author_short, title_short, journal) in completions]

        # print "%d bib entries matching %s" % (len(r), prefix)

        return r
    def run(self, edit, **args):
        view = self.view
        window = view.window()
        default_settings = sublime.load_settings("LaTeXTools.sublime-settings")

        _current_settings = [[s, get_setting(s), default_settings.get(s)]
                             for s in _toggle_settings]

        _panel_entries = [_make_panel_entry(t) for t in _current_settings]

        def toggle_setting(index):
            if index == -1:
                return
            name, value = _current_settings[index][0:2]
            new_value = not value
            message = "Set '{0}' to {1}".format(name, new_value)
            print(message)
            sublime.status_message(message)
            _current_settings[index][1] = new_value
            view.settings().set(name, new_value)
            _panel_entries[index] = _make_panel_entry(_current_settings[index])

            # keep the index (only possible with ST3)
            flags = {"selected_index": index} if _ST3 else {}
            window.show_quick_panel(_panel_entries, toggle_setting, **flags)

        window.show_quick_panel(_panel_entries, toggle_setting)
Exemple #19
0
def get_viewer():
	default_viewer = DEFAULT_VIEWERS.get(sublime.platform(), None)
	viewer_name = get_setting('viewer', default_viewer)
	if viewer_name in ['', 'default']:
		viewer_name = default_viewer

	if viewer_name is None:
		sublime.error_message('No viewer could be found for your platform. '
				'Please configure the "viewer" setting in your LaTeXTools '
				'Preferences')
		raise NoViewerException()

	try:
		viewer = get_plugin(viewer_name + '_viewer')
	except NoSuchPluginException:
		sublime.error_message('Cannot find viewer ' + viewer_name + '.\n' +
								'Please check your LaTeXTools Preferences.')
		raise NoViewerException()

	print(repr(viewer))
	
	# assume no-args constructor
	viewer = viewer()

	if not viewer.supports_platform(sublime.platform()):
		sublime.error_message(viewer_name + ' does not support the ' +
								'current platform. Please change the viewer in ' +
								'your LaTeXTools Preferences.')
		raise NoViewerException()

	return viewer
    def get_auto_completions(self, view, prefix, line):
        # Reverse, to simulate having the regex
        # match backwards (cool trick jps btw!)
        line = line[::-1]

        # Check the first location looks like a cite_, but backward
        old_style = OLD_STYLE_CITE_REGEX.match(line)

        # Do not match on plain "cite[a-zX*]*?" when autocompleting,
        # in case the user is typing something else
        if old_style and not prefix:
            return []

        try:
            completions = get_cite_completions(view)
        except NoBibFilesError:
            print("No bib files found!")
            sublime.status_message("No bib files found!")
            return []
        except BibParsingError as e:
            message = "Error occurred parsing {0}. {1}.".format(
                e.filename, e.message)
            print(message)
            traceback.print_exc()

            sublime.status_message(message)
            return []

        if prefix:
            lower_prefix = prefix.lower()
            completions = [
                c for c in completions
                if _is_prefix(lower_prefix, c)
            ]

        if len(completions) == 0:
            return []

        cite_autocomplete_format = get_setting(
            'cite_autocomplete_format', '{keyword}: {title}'
        )

        def formatted_entry(entry):
            try:
                return entry['<autocomplete_formatted>']
            except:
                return bibformat.format_entry(cite_autocomplete_format, entry)

        completions = [
            (
                formatted_entry(c),
                c['keyword']
            ) for c in completions
        ]

        if old_style:
            return completions, '{'
        else:
            return completions
Exemple #21
0
    def forward_sync(self, pdf_file, tex_file, line, col, **kwargs):
        command = get_setting("viewer_settings", {}).get(sublime.platform(), {}).get("forward_sync_command")

        if command is None:
            self.view_file(pdf_file)
            return

        self._run_command(command, pdf_file, tex_file, line, col)
Exemple #22
0
    def view_file(self, pdf_file, **kwargs):
        command = get_setting("viewer_settings", {}).get(sublime.platform(), {}).get("view_command")

        if command is None:
            sublime.error_message("You must set the command setting in viewer_settings before " "using the viewer.")
            return

        self._run_command(command, pdf_file)
Exemple #23
0
def get_texpath():
    platform_settings = get_setting(sublime.platform(), {})
    texpath = platform_settings.get("texpath", "")

    if not _ST3:
        return os.path.expandvars(texpath).encode(sys.getfilesystemencoding())
    else:
        return os.path.expandvars(texpath)
    def _get_synctex_editor(self):
        st_binary = get_sublime_exe()
        if st_binary is None:
            st_binary = get_setting('linux', {}).get('sublime', 'sublime_text')

        return '--synctex-editor-command={0} %{{input}}:%{{line}}'.format(
            st_binary
        )
def using_miktex():
    platform_settings = get_setting(sublime.platform(), {})
    distro = platform_settings.get('distro', '')

    if sublime.platform() == 'windows':
        return distro in ['miktex', '']
    else:
        return distro == 'miktex'
 def run(self, edit, **args):
     if get_setting('open_pdf_on_build', True):
         self.view.settings().set("open_pdf_on_build", False)
         sublime.status_message("Do not open PDF on build")
         print("Do not open PDF on build")
     else:
         self.view.settings().set("open_pdf_on_build", True)
         sublime.status_message("Open PDF on build")
         print("Open PDF on build")
Exemple #27
0
	def run(self):
		prefs_lin = get_setting('linux', {})

		view = self.window.active_view()
		if not is_tex_file(view.file_name()):
			sublime.error_message("%s is not a TeX source file: cannot view." % (os.path.basename(view.file_name()),))
			return
		quotes = ""# \"" MUST CHECK WHETHER WE NEED QUOTES ON WINDOWS!!!
		root = getTeXRoot.get_tex_root(view)

		rootFile, rootExt = os.path.splitext(root)
		pdfFile = quotes + rootFile + '.pdf' + quotes
		s = platform.system()
		script_path = None
		if s == "Darwin":
			# for inverse search, set up a "Custom" sync profile, using
			# "subl" as command and "%file:%line" as argument
			# you also have to put a symlink to subl somewhere on your path
			# Also check the box "check for file changes"
			viewercmd = ["open", "-a", "Skim"]
		elif s == "Windows":
			# with new version of SumatraPDF, can set up Inverse 
			# Search in the GUI: under Settings|Options...
			# Under "Set inverse search command-line", set:
			# sublime_text "%f":%l
			prefs_win = get_setting("windows", {})
			su_binary = prefs_win.get("sumatra", "SumatraPDF.exe")
			viewercmd = [su_binary, "-reuse-instance"]		
		elif s == "Linux":
			# the required scripts are in the 'evince' subdir
			script_path = os.path.join(sublime.packages_path(), 'LaTeXTools', 'evince')
			ev_sync_exec = os.path.join(script_path, 'evince_sync') # so we get inverse search
			# Get python binary if set in preferences:
			py_binary = prefs_lin["python2"] or 'python'
			sb_binary = prefs_lin["sublime"] or 'sublime-text'
			viewercmd = ['sh', ev_sync_exec, py_binary, sb_binary]
		else:
			sublime.error_message("Platform as yet unsupported. Sorry!")
			return	
		print (viewercmd + [pdfFile])
		try:
			Popen(viewercmd + [pdfFile], cwd=script_path)
		except OSError:
			sublime.error_message("Cannot launch Viewer. Make sure it is on your PATH.")
def parse_cwl_file(parse_line):
    # Get cwl file list
    # cwl_path = sublime.packages_path() + "/LaTeX-cwl"
    cwl_file_list = get_setting('cwl_list',
            [
                "tex.cwl",
                "latex-209.cwl",
                "latex-document.cwl",
                "latex-l2tabu.cwl",
                "latex-mathsymbols.cwl"
            ])

    # ST3 can use load_resource api, while ST2 do not has this api
    # so a little different with implementation of loading cwl files.
    if _ST3:
        cwl_files = ['Packages/LaTeX-cwl/%s' % x for x in cwl_file_list]
    else:
        cwl_files = [os.path.normpath(sublime.packages_path() + "/LaTeX-cwl/%s" % x) for x in cwl_file_list]

    completions = []
    for cwl in cwl_files:
        if _ST3:
            try:
                s = sublime.load_resource(cwl)
            except IOError:
                print(cwl + ' does not exist or could not be accessed')
                continue
        else:
            try:
                f = codecs.open(cwl, 'r', 'utf-8')
            except IOError:
                print(cwl + ' does not exist or could not be accessed')
                continue
            else:
                try:
                    s = u''.join(f.readlines())
                finally:
                    f.close()

        method = os.path.splitext(os.path.basename(cwl))[0]
        for line in s.split('\n'):
            line = line.strip()
            if line == '':
                continue
            if line[0] == '#':
                continue

            result = parse_line(line)
            if result is None:
                continue
            (keyword, insertion) = result
            item = (u'%s\t%s' % (keyword, method), insertion)

            completions.append(item)

    return completions
def _check_if_cwl_enabled():
    try:
        view = sublime.active_window().active_view()
    except AttributeError:
        return

    if view is None or not view.score_selector(0, "text.tex.latex"):
        return

    if get_setting('command_completion', 'prefixed', view=view) == 'never':
        return

    # Checking whether LaTeX-cwl is installed
    global CWL_COMPLETION_ENABLED
    if (
        os.path.exists(
            os.path.join(sublime.packages_path(), "LaTeX-cwl")
        ) or
        os.path.exists(
            os.path.join(
                sublime.installed_packages_path(),
                "LaTeX-cwl.sublime-package"
            )
        ) or
        os.path.exists(
            os.path.join(sublime.packages_path(), "User", "cwl")
        )
    ):
        CWL_COMPLETION_ENABLED = True
    else:
        CWL_COMPLETION_ENABLED = False
        return

    # add `\` as an autocomplete trigger
    g_settings = sublime.load_settings("Preferences.sublime-settings")
    acts = g_settings.get("auto_complete_triggers", [])

    # Whether auto trigger is already set in
    # Preferences.sublime-settings
    TEX_AUTO_COM = False
    for i in acts:
        if (
            i.get("selector") == "text.tex.latex" and
            i.get("characters") == "\\"
        ):
            TEX_AUTO_COM = True

    if not TEX_AUTO_COM:
        acts.append({
            "characters": "\\",
            "selector": "text.tex.latex"
        })
        g_settings.set("auto_complete_triggers", acts)

    # pre-load the completions
    get_cwl_completions().load_completions()
def _get_dyn_entries():
    dyn_entries = get_setting("dynamic_fillall_helper_entries", [])
    if dyn_entries:
        _filter_invalid_entries(dyn_entries)
        _update_input_entries(dyn_entries)
        dyn_regex = re.compile("(?:{0})".format(
            "|".join(entry["regex"] for entry in dyn_entries)))
        return dyn_entries, dyn_regex
    else:
        return [], None
Exemple #31
0
def _jumpto_image_file(view, window, tex_root, file_name):
    base_path = os.path.dirname(tex_root)

    image_types = get_setting("image_types",
                              ["png", "pdf", "jpg", "jpeg", "eps"])

    file_path = os.path.normpath(os.path.join(base_path, file_name))
    _, extension = os.path.splitext(file_path)
    extension = extension[1:]  # strip the leading point
    if not extension:
        for ext in image_types:
            test_path = file_path + "." + ext
            print("Test file: '{0}'".format(test_path))
            if os.path.exists(test_path):
                extension = ext
                file_path = test_path
                print("Found file: '{0}'".format(test_path))
                break
    if not os.path.exists(file_path):
        sublime.status_message("file does not exists: '{0}'".format(file_path))
        return

    def run_command(command):
        if not _ST3:
            command = str(command)
        command = shlex.split(command)
        # if $file is used, substitute it by the file path
        if "$file" in command:
            command = [file_path if c == "$file" else c for c in command]
        # if $file is not used, append the file path
        else:
            command.append(file_path)
        print("RUN: {0}".format(command))
        subprocess.Popen(command)

    psystem = sublime.platform()
    commands = get_setting("open_image_command", {}).get(psystem, None)
    print("Commands: '{0}'".format(commands))
    print("Open File: '{0}'".format(file_path))

    if commands is None:
        window.open_file(file_path)
    elif type(commands) is str:
        run_command(commands)
    else:
        for d in commands:
            print(d)
            # validate the entry
            if "command" not in d:
                message = "Invalid entry {0}, missing: 'command'"\
                    .format(str(d))
                sublime.status_message(message)
                print(message)
                continue
            # check whether the extension matches
            if "extension" in d:
                if extension == d["extension"] or\
                        extension in d["extension"]:
                    run_command(d["command"])
                    break
            # if no extension matches always run the command
            else:
                run_command(d["command"])
                break
        else:
            sublime.status_message(
                "No opening command for {0} defined".format(extension))
            window.open_file(file_path)
Exemple #32
0
    def on_query_completions(self, view, prefix, locations):
        if not CWL_COMPLETION_ENABLED:
            if CWL_COMPLETION_ENABLED is None:
                _check_if_cwl_enabled()
                if not CWL_COMPLETION_ENABLED:
                    return []
            else:
                return []

        point = locations[0]
        if not view.score_selector(point, "text.tex.latex"):
            return []

        point_before = point - len(prefix)
        char_before = view.substr(getRegion(point_before - 1, point_before))
        is_prefixed = char_before == "\\"

        line = view.substr(getRegion(view.line(point).begin(), point))
        line = line[::-1]
        is_env = bool(BEGIN_END_BEFORE_REGEX.match(line))

        # default completion level is "prefixed"
        completion_level = get_setting("command_completion", "prefixed")

        do_complete = {
            "never": False,
            "prefixed": is_prefixed or is_env,
            "always": True
        }.get(completion_level, is_prefixed or is_env)

        if not do_complete:
            return []

        # do not autocomplete if the leading backslash is escaped
        if ESCAPE_REGEX.match(line):
            # if there the autocompletion has been opened with the \ trigger
            # (no prefix) and the user has not enabled auto completion for the
            # scope, then hide the auto complete popup
            selector = view.settings().get("auto_complete_selector")
            if not prefix and not view.score_selector(point, selector):
                view.run_command("hide_auto_complete")
            return []

        # Do not do completions in actions
        if (latex_input_completions.TEX_INPUT_FILE_REGEX
                not in ENV_DONOT_AUTO_COM):
            ENV_DONOT_AUTO_COM.append(
                latex_input_completions.TEX_INPUT_FILE_REGEX)

        for rex in ENV_DONOT_AUTO_COM:
            if match(rex, line) is not None:
                return []

        # load the completions for the document
        if is_env:
            completions = CWL_COMPLETIONS.get_completions(env=True) + \
                get_own_env_completion(view)
        else:
            completions = CWL_COMPLETIONS.get_completions() + \
                get_own_command_completion(view)

        # autocompleting with slash already on line
        # this is necessary to work around a short-coming in ST where having a
        # keyed entry appears to interfere with it recognising that there is a
        # \ already on the line
        #
        # NB this may not work if there are other punctuation marks in the
        # completion
        if is_prefixed:
            completions = [(c[0], c[1][1:]) if _is_snippet(c) else c
                           for c in completions]

        return (completions, sublime.INHIBIT_WORD_COMPLETIONS
                | sublime.INHIBIT_EXPLICIT_COMPLETIONS)
Exemple #33
0
def get_tex_extensions():
    view = sublime.active_window().active_view()
    tex_file_exts = get_setting('tex_file_exts', ['.tex'])

    return [s.lower() for s in set(tex_file_exts)]
Exemple #34
0
def get_sublime_executable():
    processes = ['subl', 'sublime_text']

    def check_processes(st2_dir=None):
        if st2_dir is None or os.path.exists(st2_dir):
            for process in processes:
                try:
                    if st2_dir is not None:
                        process = os.path.join(st2_dir, process)

                    p = subprocess.Popen([process, '-v'],
                                         stdout=subprocess.PIPE,
                                         startupinfo=startupinfo,
                                         shell=shell,
                                         env=os.environ)
                except:
                    pass
                else:
                    stdout, _ = p.communicate()

                    if p.returncode == 0:
                        m = SUBLIME_VERSION.search(stdout.decode('utf8'))
                        if m and m.group(1) == version:
                            return process
        return None

    plat_settings = get_setting(sublime.platform(), {})
    sublime_executable = plat_settings.get('sublime_executable', None)

    if sublime_executable:
        return sublime_executable

    # we cache the results of the other checks, if possible
    if hasattr(get_sublime_executable, 'result'):
        return get_sublime_executable.result

    # are we on ST3
    if hasattr(sublime, 'executable_path'):
        get_sublime_executable.result = sublime.executable_path()
        return get_sublime_executable.result
    # in ST2 the Python executable is actually "sublime_text"
    elif sys.executable != 'python' and os.path.isabs(sys.executable):
        get_sublime_executable.result = sys.executable
        return get_sublime_executable.result

    # guess-work for ST2
    startupinfo = None
    shell = False
    if sublime.platform() == 'windows':
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        shell = _ST3

    version = sublime.version()

    result = check_processes()
    if result is not None:
        get_sublime_executable.result = result
        return result

    platform = sublime.platform()

    # guess the default install location for ST2 on Windows
    if platform == 'windows':
        st2_dir = os.path.expandvars("%PROGRAMFILES%\\Sublime Text 2")
        result = check_processes(st2_dir)
        if result is not None:
            get_sublime_executable.result = result
            return result
    # guess some locations for ST2 on Linux
    elif platform == 'linux':
        for path in [
                '$HOME/bin', '$HOME/sublime_text_2', '$HOME/sublime_text',
                '/opt/sublime_text_2', '/opt/sublime_text', '/usr/local/bin',
                '/usr/bin'
        ]:
            st2_dir = os.path.expandvars(path)
            result = check_processes(st2_dir)
            if result is not None:
                get_sublime_executable.result = result
                return result

    get_sublime_executable.result = None

    sublime.status_message(
        'Cannot determine the path to your Sublime installation. Please ' +
        'set the "sublime_executable" setting in your LaTeXTools.sublime-settings '
        + 'file.')

    return None
    def run(self, edit, **args):
        # Check prefs for PDF focus and sync
        keep_focus = args.get('keep_focus', get_setting('keep_focus', True))
        forward_sync = args.get('forward_sync',
                                get_setting('forward_sync', True))

        # If invoked from keybinding, we sync
        # Rationale: if the user invokes the jump command, s/he wants to see
        # the result of the compilation.
        # If the PDF viewer window is already visible, s/he probably wants to
        # sync, or s/he would have no need to invoke the command. And if it is
        # not visible, the natural way to just bring up the window without
        # syncing is by using the system's window management shortcuts.
        # As for focusing, we honor the toggles / prefs.
        from_keybinding = args.pop("from_keybinding", False)
        if from_keybinding:
            forward_sync = True
        print(from_keybinding, keep_focus, forward_sync)

        view = self.view

        if not is_tex_file(view.file_name()):
            sublime.error_message("%s is not a TeX source file: cannot jump." %
                                  (os.path.basename(view.fileName()), ))
            return

        root = getTeXRoot.get_tex_root(view)
        file_name = get_jobname(root)

        output_directory = get_output_directory(view)
        if output_directory is None:
            pdffile = os.path.join(os.path.dirname(root), file_name + u'.pdf')
        else:
            pdffile = os.path.join(output_directory, file_name + u'.pdf')

            if not os.path.exists(pdffile):
                pdffile = os.path.join(os.path.dirname(root),
                                       file_name + u'.pdf')

        if not os.path.exists(pdffile):
            print("Expected PDF file {0} not found".format(pdffile))
            return

        pdffile = os.path.realpath(pdffile)

        (line, col) = self.view.rowcol(self.view.sel()[0].end())
        print("Jump to: ", line, col)
        # column is actually ignored up to 0.94
        # HACK? It seems we get better results incrementing line
        line += 1

        # issue #625: we need to pass the path to the file to the viewer when
        # there are files in subfolders of the main folder.
        # Thanks rstein and arahlin for this code!
        srcfile = self.view.file_name()

        try:
            viewer = get_viewer()
        except NoViewerException:
            return

        if forward_sync:
            try:
                viewer.forward_sync(pdffile,
                                    srcfile,
                                    line,
                                    col,
                                    keep_focus=keep_focus)
            except (AttributeError, NotImplementedError):
                try:
                    viewer.view_file(pdffile, keep_focus=keep_focus)
                except (AttributeError, NotImplementedError):
                    traceback.print_exc()
                    sublime.error_message(
                        'Your viewer does not appear to be a proper'
                        'LaTeXTools viewer plugin. '
                        'Please contact the plugin author.')
                    return
        else:
            try:
                viewer.view_file(pdffile, keep_focus=keep_focus)
            except (AttributeError, NotImplementedError):
                traceback.print_exc()
                sublime.error_message(
                    'Your viewer does not appear to be a proper'
                    'LaTeXTools viewer plugin. '
                    'Please contact the plugin author.')
                return

        if keep_focus:
            try:
                if viewer.supports_keep_focus():
                    return
            except (AttributeError, NotImplementedError):
                pass

            focus_st()
    def run(self, edit, insert_char=""):
        view = self.view
        point = view.sel()[0].b
        # Only trigger within LaTeX
        # Note using score_selector rather than match_selector
        if not view.score_selector(point, "text.tex.latex"):
            return

        if insert_char:
            # append the insert_char to the end of the current line if it
            # is given so this works when being triggered by pressing "{"
            point += view.insert(edit, point, insert_char)

            do_completion = get_setting("fill_auto_trigger", True)

            if not do_completion:
                add_closing_bracket(view, edit)
                return

        prefix, completions = parse_completions(
            view,
            view.substr(sublime.Region(view.line(point).a, point)))

        if len(completions) == 0:
            result = []
        elif not type(completions[0]) is tuple:
            result = completions
        else:
            tex_root = getTeXRoot.get_tex_root(self.view)
            if tex_root:
                root_path = os.path.dirname(tex_root)
            else:
                print("Can't find TeXroot. Assuming current directory is {0}".format(os.curdir))
                root_path = os.curdir

            result = [[
                # Replace backslash with forward slash to fix Windows paths
                # LaTeX does not support forward slashes in paths
                os.path.normpath(os.path.join(relpath, filename)).replace('\\', '/'),
                os.path.normpath(os.path.join(root_path, relpath, filename))
            ] for relpath, filename in completions]

        def on_done(i):
            # Doing Nothing
            if i < 0:
                return
            if type(result[i]) is list:  # if result[i] is a list, it comes from input, include and includegraphics
                key = result[i][0]
            else:
                key = result[i]

            # close bracket
            if insert_char:
                key += "}"

            startpos = point - len(prefix)
            view.run_command("latex_tools_replace", {"a": startpos, "b": point, "replacement": key})
            caret = view.sel()[0].b
            view.sel().subtract(view.sel()[0])
            view.sel().add(sublime.Region(caret, caret))

        # autocomplete bracket if we aren't doing anything
        if not result and insert_char:
            add_closing_bracket(view, edit)
        else:
            view.window().show_quick_panel(result, on_done)
 def is_enabled(self):
     return get_setting("glossary_auto_trigger", True)
Exemple #38
0
    def complete_auto_match(self, view, edit, insert_char):
        '''
        Completes brackets if auto_match is enabled; also implements the
        "smart_bracket_auto_trigger" logic, which tries to complete the nearest
        open bracket intelligently.

        :param view:
            the current view

        :param edit:
            the current edit

        :param insert_char:
            the character to try to automatch
        '''
        if sublime.load_settings('Preferences.sublime-settings').get(
                'auto_match_enabled', True):
            # simple case: we have an insert char, insert closing char,
            # if its defined
            if insert_char:
                self.insert_at_end(view, edit,
                                   self.get_match_char(insert_char))

                # if the insert_char is a bracket, move cursor to middle of
                # bracket and return
                if insert_char in self.MATCH_CHARS:
                    new_regions = []
                    for sel in view.sel():
                        if not sel.empty():
                            new_regions.append(sel)
                        else:
                            new_point = sel.end() - 1
                            new_regions.append(getRegion(new_point, new_point))

                    self.update_selections(view, new_regions)
                    return
            elif get_setting('smart_bracket_auto_trigger', True):
                # more complex: if we do not have an insert_char, try to close
                # the nearest bracket that occurs before each selection
                new_regions = []

                for sel in view.sel():
                    word_region = self.get_current_word(view, sel)
                    close_bracket = self.get_closing_bracket(view, word_region)
                    # we should close the bracket
                    if close_bracket:
                        # insert the closing bracket
                        view.insert(edit, word_region.end(), close_bracket)

                        if sel.empty():
                            if word_region.empty():
                                new_regions.append(
                                    getRegion(word_region.end(),
                                              word_region.end()))
                            else:
                                new_point = word_region.end() + \
                                    len(close_bracket)
                                new_regions.append(
                                    getRegion(new_point, new_point))
                        else:
                            new_regions.append(
                                getRegion(
                                    sel.begin(),
                                    word_region.end() + len(close_bracket)))
                    else:
                        new_regions.append(sel)

                self.update_selections(view, new_regions)
Exemple #39
0
 def _ensure_okular(self, **kwargs):
     if not self._is_okular_running():
         self._run_okular(**kwargs)
         time.sleep(get_setting('linux', {}).get('sync_wait') or 1.0)
Exemple #40
0
def get_tex_extensions():
    tex_file_exts = get_setting('tex_file_exts', ['.tex'])

    return [s.lower() for s in set(tex_file_exts)]
def get_ref_completions(view, point, autocompleting=False):
    # Get contents of line from start up to point
    line = view.substr(sublime.Region(view.line(point).a, point))
    # print line

    # Reverse, to simulate having the regex
    # match backwards (cool trick jps btw!)
    line = line[::-1]
    #print line

    # Check the first location looks like a ref, but backward
    rex = OLD_STYLE_REF_REGEX
    expr = match(rex, line)
    # print expr

    if expr:
        # Do not match on plain "ref" when autocompleting,
        # in case the user is typing something else
        if autocompleting and re.match(
                r"p?fer(?:" + _ref_special_commands + r")?\\?", expr):
            raise UnrecognizedRefFormatError()
        # Return the matched bits, for mangling
        prefix, has_p, special_command = rex.match(expr).groups()
        preformatted = False
        if prefix:
            prefix = prefix[::-1]  # reverse
            prefix = prefix[1:]  # chop off "_"
        else:
            prefix = ""
        #print prefix, has_p, special_command

    else:
        # Check to see if the location matches a preformatted "\ref{blah"
        rex = NEW_STYLE_REF_REGEX
        expr = match(rex, line)

        if not expr:
            raise UnrecognizedRefFormatError()

        preformatted = True
        # Return the matched bits (barely needed, in this case)
        prefix, special_command, has_p = rex.match(expr).groups()
        if prefix:
            prefix = prefix[::-1]  # reverse
        else:
            prefix = ""
        #print prefix, has_p, special_command

    pre_snippet = "\\" + special_command[::-1] + "ref{"
    post_snippet = "}"

    # If we captured a parenthesis, we need to put it back in
    # However, if the user had paren automatching, we don't want to add
    # another one. So by default we don't, unless the user tells us to
    # in the settings.
    # (HACKISH: I don't actually remember why we matched the initial paren!)
    if has_p:
        pre_snippet = "(" + pre_snippet
        add_paren = get_setting("ref_add_parenthesis", False)
        if add_paren:
            post_snippet = post_snippet + ")"

    if not preformatted:
        # Replace ref_blah with \ref{blah
        # The "latex_tools_replace" command is defined in latex_ref_cite_completions.py
        view.run_command(
            "latex_tools_replace", {
                "a": point - len(expr),
                "b": point,
                "replacement": pre_snippet + prefix
            })
        # save prefix begin and endpoints points
        new_point_a = point - len(expr) + len(pre_snippet)
        new_point_b = new_point_a + len(prefix)
#        view.end_edit(ed)

    else:
        # Don't include post_snippet if it's already present
        suffix = view.substr(sublime.Region(point, point + len(post_snippet)))
        new_point_a = point - len(prefix)
        new_point_b = point
        if post_snippet == suffix:
            post_snippet = ""

    completions = []
    # Check the file buffer first:
    #    1) in case there are unsaved changes
    #    2) if this file is unnamed and unsaved, get_tex_root will fail
    view.find_all(r'\\label\{([^\{\}]+)\}', 0, '\\1', completions)

    root = getTeXRoot.get_tex_root(view)
    if root:
        print("TEX root: " + repr(root))
        find_labels_in_files(os.path.dirname(root), root, completions)

    # remove duplicates
    completions = list(set(completions))

    return completions, prefix, post_snippet, new_point_a, new_point_b
    def _on_main_thread(self, results):
        builder_name = get_setting('builder', 'traditional', view=self.view)

        if builder_name in ['', 'default']:
            builder_name = 'traditional'

        builder_settings = get_setting('builder_settings', view=self.view)
        builder_path = get_setting('builder_path', view=self.view)

        if builder_name in ['simple', 'traditional', 'script']:
            builder_path = None
        else:
            bld_path = os.path.join(sublime.packages_path(), builder_path)
            add_plugin_path(bld_path)

        builder_name = _classname_to_internal_name(builder_name)

        try:
            get_plugin('{0}_builder'.format(builder_name))
            builder_available = True
        except NoSuchPluginException:
            traceback.print_exc()
            builder_available = False

        results.append(
            [[u'Builder', u'Status'],
             [builder_name,
              u'available' if builder_available else u'missing']])

        if builder_path:
            results.append([[u'Builder Path'], [builder_path]])

        if builder_settings is not None:
            table = [[u'Builder Setting', u'Value']]
            for key in sorted(builder_settings.keys()):
                value = builder_settings[key]
                table.append([key, value])
            results.append(table)

        # is current view a TeX file?
        view = self.view
        if view.score_selector(0, 'text.tex.latex') != 0:
            tex_root = get_tex_root(view)
            tex_directives = parse_tex_directives(
                tex_root,
                multi_values=['options'],
                key_maps={'ts-program': 'program'})

            results.append([[u'TeX Root'], [tex_root]])

            results.append([[u'LaTeX Engine'],
                            [
                                tex_directives.get(
                                    'program',
                                    get_setting('program', 'pdflatex',
                                                self.view))
                            ]])

            table = [[u'LaTeX Output Setting', u'Value']]
            output_directory = get_output_directory(tex_root)
            if output_directory:
                table.append(['output_directory', output_directory])
            aux_directory = get_aux_directory(tex_root)
            if aux_directory:
                table.append(['aux_directory', aux_directory])
            jobname = get_jobname(tex_root)
            if jobname and jobname != os.path.splitext(
                    os.path.basename(tex_root))[0]:
                table.append(['jobname', jobname])

            if len(table) > 1:
                results.append(table)

            options = get_setting('builder_settings', {}, self.view).\
                get('options', [])
            options.extend(tex_directives.get('options', []))

            if len(options) > 0:
                table = [[u'LaTeX Options']]
                for option in options:
                    table.append([option])

                results.append(table)

        default_viewer = DEFAULT_VIEWERS.get(sublime.platform(), None)
        viewer_name = get_setting('viewer', default_viewer)
        if viewer_name in ['', 'default']:
            viewer_name = default_viewer

        try:
            viewer_plugin = get_plugin(viewer_name + '_viewer')
            viewer_available = True
        except NoSuchPluginException:
            viewer_available = False

        viewer_location = 'N/A'
        if viewer_available:
            if viewer_name == 'command':
                # assume the command viewer is always valid
                viewer_location = 'N/A'
            elif viewer_name in ('evince', 'okular', 'zathura'):
                viewer_location = which(viewer_name)
                viewer_available = bool(viewer_location)
            elif viewer_name == 'preview':
                viewer_location = '/Applications/Preview.app'

                if not os.path.exists(viewer_location):
                    try:
                        viewer_location = check_output([
                            'osascript', '-e', 'POSIX path of '
                            '(path to app id "com.apple.Preview")'
                        ],
                                                       use_texpath=False)
                    except subprocess.CalledProcessError:
                        viewer_location = None

                viewer_available = False \
                    if not viewer_location else os.path.exists(viewer_location)
            elif viewer_name == 'skim':
                viewer_location = '/Applications/Skim.app'

                if not os.path.exists(viewer_location):
                    try:
                        viewer_location = check_output([
                            'osascript', '-e', 'POSIX path of '
                            '(path to app id "net.sourceforge.skim-app.skim")'
                        ],
                                                       use_texpath=False)
                    except subprocess.CalledProcessError:
                        viewer_location = None

                viewer_available = False \
                    if not viewer_location else os.path.exists(viewer_location)
            elif viewer_name == 'sumatra':
                sumatra_exe = get_setting('viewer_settings', {}).\
                    get('sumatra', get_setting('windows', {}).
                        get('sumatra', 'SumatraPDF.exe')) or \
                    'SumatraPDF.exe'

                viewer_location = which(sumatra_exe)
                if not bool(viewer_location):
                    viewer_location = viewer_plugin()._find_sumatra_exe()
                    viewer_available = bool(viewer_location)

        if not viewer_available:
            viewer_location = 'N/A'

        results.append([[u'Viewer', u'Status', u'Location'],
                        [
                            viewer_name,
                            u'available' if viewer_available else u'missing',
                            viewer_location
                        ]])

        if callable(self.on_done):
            self.on_done(results)
def _get_texpath(view):
    texpath = get_setting(sublime.platform(), {}, view).get('texpath')
    return expand_vars(texpath) if texpath is not None else None
def run_plugin_command(command, *args, **kwargs):
    '''
    This function is intended to run a command against a user-configurable list
    of bibliography plugins set using the `bibliography` setting.

    Parameters:
        `command`: a string representing the command to invoke, which should
            generally be the name of a function to be called on the plugin
                class.
        `*args`: the args to pass to the function
        `**kwargs`: the keyword args to pass to the function

    Additionally, the following keyword parameters can be specified to control
    how this function works:
        `stop_on_first`: if True (default), no more attempts will be made to
            run the command after the first plugin that returns a non-None
            result
        `expect_result`: if True (default), a BibPluginError will be raised if
            no plugin returns a non-None result

    Example:
        run_plugin_command('get_entries', *bib_files)
        This will attempt to invoke the `get_entries` method of any configured
        plugin, passing in the discovered bib_files, and returning the result.

    The general assumption of this function is that we only care about the
    first valid result returned from a plugin and that plugins that should not
    handle a request will either not implement the method or implement a
    version of the method which raises a NotImplementedError if that plugin
    should not handle the current situation.
    '''
    stop_on_first = kwargs.pop('stop_on_first', True)
    expect_result = kwargs.pop('expect_result', True)

    def _run_command(plugin_name):
        plugin = None
        try:
            plugin = latextools_plugin.get_plugin(plugin_name)
        except latextools_plugin.NoSuchPluginException:
            pass

        if not plugin:
            error_message = 'Could not find bibliography plugin named {0}. Please ensure your LaTeXTools.sublime-settings is configured correctly.'.format(
                plugin_name)
            print(error_message)
            raise BibPluginError(error_message)

        # instantiate plugin
        try:
            plugin = plugin()
        except:
            error_message = 'Could not instantiate {0}. {0} must have a no-args __init__ method'.format(
                type(plugin).__name__, )

            print(error_message)
            raise BibPluginError(error_message)

        try:
            result = getattr(plugin, command)(*args, **kwargs)
        except TypeError as e:
            if "'{0}()'".format(command) in str(e):
                error_message = '{1} is not properly implemented by {0}.'.format(
                    type(plugin).__name__, command)

                print(error_message)
                raise BibPluginError(error_message)
            else:
                reraise(*sys.exc_info())
        except AttributeError as e:
            if "'{0}'".format(command) in str(e):
                error_message = '{0} does not implement `{1}`'.format(
                    type(plugin).__name__, command)

                print(error_message)
                raise BibPluginError(error_message)
            else:
                reraise(*sys.exc_info())
        except NotImplementedError:
            return None

        return result

    plugins = get_setting('bibliography', ['traditional'])
    if not plugins:
        print('bibliography setting is blank. Loading traditional plugin.')
        plugins = 'traditional'

    result = None
    if isinstance(plugins, strbase):
        if not plugins.endswith('_bibliography'):
            plugins = '{0}_bibliography'.format(plugins)
        result = _run_command(plugins)
    else:
        for plugin_name in plugins:
            if not plugin_name.endswith('_bibliography'):
                plugin_name = '{0}_bibliography'.format(plugin_name)
            try:
                result = _run_command(plugin_name)
            except BibPluginError:
                continue
            if stop_on_first and result is not None:
                break

    if expect_result and result is None:
        raise BibPluginError(
            "Could not find a plugin to handle '{0}'. See the console for more details"
            .format(command))

    return result
Exemple #45
0
    def run(self, edit, **args):
        # Check prefs for PDF focus and sync
        keep_focus = get_setting('keep_focus', True)
        forward_sync = get_setting('forward_sync', True)

        prefs_lin = get_setting("linux", {})
        prefs_win = get_setting("windows", {})

        # If invoked from keybinding, we sync
        # Rationale: if the user invokes the jump command, s/he wants to see the result of the compilation.
        # If the PDF viewer window is already visible, s/he probably wants to sync, or s/he would have no
        # need to invoke the command. And if it is not visible, the natural way to just bring up the
        # window without syncing is by using the system's window management shortcuts.
        # As for focusing, we honor the toggles / prefs.
        from_keybinding = args[
            "from_keybinding"] if "from_keybinding" in args else False
        if from_keybinding:
            forward_sync = True
        print(from_keybinding, keep_focus, forward_sync)

        if not is_tex_file(self.view.file_name()):
            sublime.error_message("%s is not a TeX source file: cannot jump." %
                                  (os.path.basename(view.fileName()), ))
            return
        quotes = "\""

        root = getTeXRoot.get_tex_root(self.view)
        print("!TEX root = ",
              repr(root))  # need something better here, but this works.
        rootName, rootExt = os.path.splitext(root)
        pdffile = rootName + u'.pdf'
        (line, col) = self.view.rowcol(self.view.sel()[0].end())
        print("Jump to: ", line, col)
        # column is actually ignored up to 0.94
        # HACK? It seems we get better results incrementing line
        line += 1

        # issue #625: we need to pass the path to the file to the viewer when
        # there are files in subfolders of the main folder.
        # Thanks rstein and arahlin for this code!
        srcfile = self.view.file_name()

        # Query view settings to see if we need to keep focus or let the PDF viewer grab it
        # By default, we respect settings in Preferences

        # platform-specific code:
        plat = sublime_plugin.sys.platform
        if plat == 'darwin':
            options = ["-r", "-g"] if keep_focus else ["-r"]
            if forward_sync:
                path_to_skim = '/Applications/Skim.app/'
                if not os.path.exists(path_to_skim):
                    path_to_skim = subprocess.check_output([
                        'osascript', '-e',
                        'POSIX path of (path to app id "net.sourceforge.skim-app.skim")'
                    ]).decode("utf8")[:-1]
                subprocess.Popen([
                    os.path.join(path_to_skim,
                                 "Contents/SharedSupport/displayline")
                ] + options + [str(line), pdffile, srcfile])
            else:
                skim = os.path.join(sublime.packages_path(), 'LaTeXTools',
                                    'skim', 'displayfile')
                subprocess.Popen(['sh', skim] + options + [pdffile])
        elif plat == 'win32':
            # determine if Sumatra is running, launch it if not
            print("Windows, Calling Sumatra")

            si = subprocess.STARTUPINFO()
            si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            si.wShowWindow = 4  #constant for SHOWNOACTIVATE

            su_binary = prefs_win.get("sumatra",
                                      "SumatraPDF.exe") or 'SumatraPDF.exe'
            startCommands = [su_binary, "-reuse-instance"]
            if forward_sync:
                startCommands.append("-forward-search")
                startCommands.append(srcfile)
                startCommands.append(str(line))

            startCommands.append(pdffile)

            subprocess.Popen(startCommands, startupinfo=si)

            if keep_focus:
                self.focus_st()
        elif 'linux' in plat:  # for some reason, I get 'linux2' from sys.platform
            print("Linux!")

            # the required scripts are in the 'evince' subdir
            ev_path = os.path.join(sublime.packages_path(), 'LaTeXTools',
                                   'evince')
            ev_fwd_exec = os.path.join(ev_path, 'evince_forward_search')
            ev_sync_exec = os.path.join(ev_path,
                                        'evince_sync')  # for inverse search!
            #print ev_fwd_exec, ev_sync_exec

            # Run evince if either it's not running, or if focus PDF was toggled
            # Sadly ST2 has Python <2.7, so no check_output:
            running_apps = subprocess.Popen(
                ['ps', 'xw'], stdout=subprocess.PIPE).communicate()[0]
            # If there are non-ascii chars in the output just captured, we will fail.
            # Thus, decode using the 'ignore' option to simply drop them---we don't need them
            running_apps = running_apps.decode(
                sublime_plugin.sys.getdefaultencoding(), 'ignore')

            # Run scripts through sh because the script files will lose their exec bit on github

            # Get python binary if set:
            py_binary = prefs_lin["python2"] or 'python'
            sb_binary = prefs_lin["sublime"] or 'sublime-text'
            # How long we should wait after launching sh before syncing
            sync_wait = prefs_lin["sync_wait"] or 1.0

            evince_running = ("evince " + pdffile in running_apps)
            if (not keep_focus) or (not evince_running):
                print("(Re)launching evince")
                subprocess.Popen(
                    ['sh', ev_sync_exec, py_binary, sb_binary, pdffile],
                    cwd=ev_path)
                print("launched evince_sync")
                if not evince_running:  # Don't wait if we have already shown the PDF
                    time.sleep(sync_wait)
            if forward_sync:
                subprocess.Popen(
                    [py_binary, ev_fwd_exec, pdffile,
                     str(line), srcfile])
            if keep_focus:
                self.focus_st()
        else:  # ???
            pass
Exemple #46
0
 def __init__(self, tex_root):
     self.tex_root = tex_root
     # although this could change, currently only the value when the
     # cache is created is relevant
     self.hide_cache = get_setting('hide_local_cache', True)
     super(LocalCache, self).__init__()
Exemple #47
0
def _get_plugin_paths():
    plugin_paths = get_setting('plugin_paths', [])
    return plugin_paths
Exemple #48
0
    def run(self, cmd="", file_regex="", path=""):

        # Try to handle killing
        with self.proc_lock:
            if self.proc:  # if we are running, try to kill running process
                self.output("\n\n### Got request to terminate compilation ###")
                if sublime.platform() == 'windows':
                    startupinfo = subprocess.STARTUPINFO()
                    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                    subprocess.call(
                        'taskkill /t /f /pid {pid}'.format(pid=self.proc.pid),
                        startupinfo=startupinfo,
                        shell=True)
                else:
                    os.killpg(self.proc.pid, signal.SIGTERM)
                self.proc = None
                return
            else:  # either it's the first time we run, or else we have no running processes
                self.proc = None

        view = self.view = self.window.active_view()

        if view.is_dirty():
            print("saving...")
            view.run_command('save')  # call this on view, not self.window

        if view.file_name() is None:
            sublime.error_message(
                'Please save your file before attempting to build.')
            return

        self.file_name = getTeXRoot.get_tex_root(view)
        if not os.path.isfile(self.file_name):
            sublime.error_message(self.file_name + ": file not found.")
            return

        self.tex_base, self.tex_ext = os.path.splitext(self.file_name)
        tex_dir = os.path.dirname(self.file_name)

        if not is_tex_file(self.file_name):
            sublime.error_message(
                "%s is not a TeX source file: cannot compile." %
                (os.path.basename(view.file_name()), ))
            return

        # Output panel: from exec.py
        if not hasattr(self, 'output_view'):
            self.output_view = self.window.get_output_panel("exec")

        # Dumb, but required for the moment for the output panel to be picked
        # up as the result buffer
        self.window.get_output_panel("exec")

        self.output_view.settings().set(
            "result_file_regex", "^([^:\n\r]*):([0-9]+):?([0-9]+)?:? (.*)$")
        # self.output_view.settings().set("result_line_regex", line_regex)
        self.output_view.settings().set("result_base_dir", tex_dir)

        self.window.run_command("show_panel", {"panel": "output.exec"})

        self.output_view.settings().set("result_file_regex", file_regex)

        self.plat = sublime.platform()
        if self.plat == "osx":
            self.encoding = "UTF-8"
        elif self.plat == "windows":
            self.encoding = getOEMCP()
        elif self.plat == "linux":
            self.encoding = "UTF-8"
        else:
            sublime.error_message("Platform as yet unsupported. Sorry!")
            return

        # Get platform settings, builder, and builder settings
        platform_settings = get_setting(self.plat, {})
        builder_name = get_setting("builder", "traditional")
        self.hide_panel_level = get_setting("hide_build_panel", "never")
        self.display_bad_boxes = get_setting("display_bad_boxes", False)
        # This *must* exist, so if it doesn't, the user didn't migrate
        if builder_name is None:
            sublime.error_message(
                "LaTeXTools: you need to migrate your preferences. See the README file for instructions."
            )
            return
        # Default to 'traditional' builder
        if builder_name in ['', 'default']:
            builder_name = 'traditional'
        # relative to ST packages dir!
        builder_path = get_setting("builder_path", "")
        builder_file_name = builder_name + 'Builder.py'
        builder_class_name = builder_name.capitalize() + 'Builder'
        builder_settings = get_setting("builder_settings", {})

        # parse root for any %!TEX directives
        tex_directives = parse_tex_directives(
            self.file_name,
            multi_values=['options'],
            key_maps={'ts-program': 'program'})

        # determine the engine
        engine = tex_directives.get(
            'program', builder_settings.get("program", "pdflatex"))

        engine = engine.lower()

        # Sanity check: if "strange" engine, default to pdflatex (silently...)
        if engine not in [
                'pdflatex', "pdftex", 'xelatex', 'xetex', 'lualatex', 'luatex'
        ]:
            engine = 'pdflatex'

        options = builder_settings.get("options", [])
        if isinstance(options, strbase):
            options = [options]

        if 'options' in tex_directives:
            options.extend(tex_directives['options'])

        # Read the env option (platform specific)
        builder_platform_settings = builder_settings.get(self.plat)
        if builder_platform_settings:
            self.env = builder_platform_settings.get("env", None)
        else:
            self.env = None

        # Safety check: if we are using a built-in builder, disregard
        # builder_path, even if it was specified in the pref file
        if builder_name in ['simple', 'traditional', 'script', 'default', '']:
            builder_path = None

        # Now actually get the builder
        ltt_path = os.path.join(sublime.packages_path(), 'LaTeXTools',
                                'builders')
        if builder_path:
            bld_path = os.path.join(sublime.packages_path(), builder_path)
        else:
            bld_path = ltt_path
        bld_file = os.path.join(bld_path, builder_file_name)

        if not os.path.isfile(bld_file):
            sublime.error_message("Cannot find builder " + builder_name + ".\n" \
                      "Check your LaTeXTools Preferences")
            return

        # We save the system path and TEMPORARILY add the builders path to it,
        # so we can simply "import pdfBuilder" in the builder module
        # For custom builders, we need to add both the LaTeXTools builders
        # path, as well as the custom path specified above.
        # The mechanics are from http://effbot.org/zone/import-string.htm

        syspath_save = list(sys.path)
        sys.path.insert(0, ltt_path)
        if builder_path:
            sys.path.insert(0, bld_path)
        builder_module = __import__(builder_name + 'Builder')
        sys.path[:] = syspath_save

        print(repr(builder_module))
        builder_class = getattr(builder_module, builder_class_name)
        print(repr(builder_class))
        # We should now be able to construct the builder object
        self.builder = builder_class(self.file_name, self.output, engine,
                                     options, tex_directives, builder_settings,
                                     platform_settings)

        # Restore Python system path
        sys.path[:] = syspath_save

        # Now get the tex binary path from prefs, change directory to
        # that of the tex root file, and run!
        self.path = platform_settings['texpath']
        os.chdir(tex_dir)
        CmdThread(self).start()
        print(threading.active_count())
Exemple #49
0
def plugin_loaded():
    # get additional entries from the settings
    _setting_entries = get_setting("fillall_helper_entries", [])
    _filter_invalid_entries(_setting_entries)
    _fillall_entries.extend(_setting_entries)

    _fillall_entries.extend([
        # input/include
        {
            "regex": r'(?:edulcni|tupni)\\',
            "extensions": [e[1:] for e in get_tex_extensions()],
            "strip_extensions": [".tex"]
        },
        # includegraphics
        {
            "regex":
            r'(?:\][^{}\[\]]*\[)?scihpargedulcni\\',
            "extensions":
            get_setting("image_types", ["pdf", "png", "jpeg", "jpg", "eps"])
        },
        # import/subimport
        {
            "regex": r'\*?(?:tropmibus)\\',
            "extensions": [e[1:] for e in get_tex_extensions()],
            "strip_extensions": [".tex"],
            "post_process": "path_only"
        },
        {
            "regex":
            r'\}[^{}\[\]]*\{\*?(?:tropmi|morftupni|morfedulcni)?bus\\',
            "extensions": [e[1:] for e in get_tex_extensions()],
            "strip_extensions": [".tex"],
            "post_regex": (r'\\sub(?:import|includefrom|inputfrom)\*?'
                           r'\{([^{}\[\]]*)\}\{[^\}]*?$'),
            "folder":
            "$base/$_1"
        },
        {
            "regex":
            r'\}[^{}\[\]]*\{\*?(?:tropmi|morftupni|morfedulcni)\\',
            "extensions": [e[1:] for e in get_tex_extensions()],
            "strip_extensions": [".tex"],
            "post_regex": (r'\\(?:import|includefrom|inputfrom)\*?'
                           r'\{([^{}\[\]]*)\}\{[^\}]*?$'),
            "folder":
            "$_1"
        },
        {
            "regex": r'(?:\][^{}\[\]]*\[)?ecruoserbibdda\\',
            "extensions": ["bib"]
        },
        {
            "regex": r'yhpargoilbib\\',
            "extensions": ["bib"],
            "strip_extensions": [".bib"],
            "comma_separated": True
        }
    ])

    # update the fields of the entries
    _update_input_entries(_fillall_entries)

    _fillall_entries.extend([{
        "regex": r'([^{}\[\]]*)\{(?:\][^{}\[\]]*\[)?ssalctnemucod\\',
        "type": "cached",
        "cache_name": "cls"
    }, {
        "regex": r'([^{}\[\]]*)\{(?:\][^{}\[\]]*\[)?egakcapesu\\',
        "type": "cached",
        "cache_name": "pkg"
    }, {
        "regex": r'([^{}\[\]]*)\{elytsyhpargoilbib\\',
        "type": "cached",
        "cache_name": "bst"
    }])

    global _TEX_INPUT_GROUP_MAPPING, TEX_INPUT_FILE_REGEX
    _TEX_INPUT_GROUP_MAPPING = dict(
        (i, v) for i, v in enumerate(_fillall_entries))
    TEX_INPUT_FILE_REGEX = re.compile("(?:{0})".format("|".join(
        entry["regex"] for entry in _fillall_entries)))
def parse_completions(view, line):
    # reverse line, copied from latex_cite_completions, very cool :)
    line = line[::-1]

    # Do matches!
    search = TEX_INPUT_FILE_REGEX.match(line)

    installed_cls = []
    installed_bst = []
    installed_pkg = []
    input_file_types = None

    if search is not None:
        (   include_filter,
            input_filter,
            image_filter,
            svg_filter,
            addbib_filter,
            bib_filter,
            cls_filter,
            pkg_filter,
            bst_filter) = search.groups()
    else:
        return '', []

    # it isn't always correct to include the extension in the output filename
    # esp. with \bibliography{}; here we provide a mechanism to permit this
    filter_exts = []

    if include_filter is not None:
        # if is \include
        prefix = include_filter[::-1]
        # filter the . from the start of the extention
        input_file_types = [e[1:] for e in get_tex_extensions()]
        # only cut off the .tex extension
        filter_exts = ['.tex']
    elif input_filter is not None:
        # if is \input search type set to tex
        prefix = input_filter[::-1]
        # filter the . from the start of the extension
        input_file_types = [e[1:] for e in get_tex_extensions()]
        # only cut off the .tex extension
        filter_exts = ['.tex']
    elif image_filter is not None:
        # if is \includegraphics
        prefix = image_filter[::-1]
        # Load image types from configurations
        # In order to user input, "image_types" must be set in
        # LaTeXTools.sublime-settings configuration file or the
        # project settings for the current view.
        input_file_types = get_setting('image_types', [
                'pdf', 'png', 'jpeg', 'jpg', 'eps'
            ])
    elif svg_filter is not None:
        # if is \includesvg
        prefix = svg_filter[::-1]
        # include only svg files
        input_file_types = ['svg']
        # cut off the svg extention
        filter_exts = ['.svg']
    elif addbib_filter is not None or bib_filter is not None:
        # For bibliography
        if addbib_filter is not None:
            prefix = addbib_filter[::-1]
        else:
            prefix = ''
            bib_filter[::-1]
            filter_exts = ['.bib']
        input_file_types = ['bib']
    elif cls_filter is not None or pkg_filter is not None or bst_filter is not None:
        # for packages, classes and bsts
        if _ST3:
            cache_path = os.path.normpath(
                os.path.join(
                    sublime.cache_path(),
                    "LaTeXTools"
                ))
        else:
            cache_path = os.path.normpath(
                os.path.join(
                    sublime.packages_path(),
                    "User"
                ))

        pkg_cache_file = os.path.normpath(
            os.path.join(cache_path, 'pkg_cache.cache' if _ST3 else 'latextools_pkg_cache.cache'))

        cache = None
        if not os.path.exists(pkg_cache_file):
            gen_cache = sublime.ok_cancel_dialog("Cache files for installed packages, "
                + "classes and bibliographystyles do not exists, "
                + "would you like to generate it? After generating complete, please re-run this completion action!"
            )

            if gen_cache:
                sublime.active_window().run_command("latex_gen_pkg_cache")
                completions = []
        else:
            with open(pkg_cache_file) as f:
                cache = json.load(f)

        if cache is not None:
            if cls_filter is not None:
                installed_cls = cache.get("cls")
            elif bst_filter is not None:
                installed_bst = cache.get("bst")
            else:
                installed_pkg = cache.get("pkg")

        prefix = ''
    else:
        prefix = ''

    if len(installed_cls) > 0:
        completions = installed_cls
    elif len(installed_bst) > 0:
        completions = installed_bst
    elif len(installed_pkg) > 0:
        completions = installed_pkg
    elif input_file_types is not None:
        root = getTeXRoot.get_tex_root(view)
        if root:
            completions = get_file_list(root, input_file_types, filter_exts)
        else:
            # file is unsaved
            completions = []

    return prefix, completions
Exemple #51
0
def _latextools_module_hack():
    '''
    Context manager to ensure sys.modules has certain white-listed modules,
    most especially latextools_plugins. This exposes some of the modules in
    LaTeXTools to plugins. It is intended primarily to expose library-esque
    functionality, such as the getTeXRoot module, but can be configured by
    the user as-needed.
    '''
    # add any white-listed plugins to sys.modules under their own name
    plugins_whitelist = get_setting(
        'plugins_whitelist',
        ['external', 'getTeXRoot', 'kpsewhich', 'latextools_utils'])

    # always include latextools_pluing
    plugins_whitelist.append('latextools_plugin')
    overwritten_modules = {}

    whitelist = [(name, None) for name in plugins_whitelist]
    whitelist.extend(internal._WHITELIST_ADDED)

    # put the directory containing this file on the sys.path
    __dir__ = os.path.dirname(__file__)

    # handles ST2s relative directory
    if __dir__ == '.':
        __dir__ = os.path.join(sublime.packages_path(), 'LaTeXTools')

    # insert the LaTeXTools directory on the path
    sys.path.insert(0, __dir__)
    for name, module in whitelist:
        if callable(module):
            module = module()

        if name in sys.modules:
            overwritten_modules[name] = sys.modules[name]

        # attempting to autoload module
        if module is None:
            # if the module has already been loaded by ST, we just use that
            latextools_module_name = _get_sublime_module_name(__dir__, name)
            if latextools_module_name in sys.modules:
                sys.modules[name] = sys.modules[latextools_module_name]
            else:
                try:
                    sys.modules[name] = _load_module(name, name, __dir__)
                except ImportError:
                    print(
                        'An error occurred while trying to load white-listed '
                        'module {0}'.format(name))
                    traceback.print_exc()
        else:
            sys.modules[name] = module

    # remove the LaTeXTools directory from the path
    sys.path.pop(0)

    yield

    # restore any temporarily overwritten modules and clear our loaded modules
    for module in plugins_whitelist:
        if _get_sublime_module_name(__dir__, module) != module:
            sys.modules[module] = None
        if module in overwritten_modules:
            sys.modules[module] = overwritten_modules[module]
Exemple #52
0
    def run(self,
            file_regex="",
            program=None,
            builder=None,
            command=None,
            env=None,
            path=None,
            script_commands=None,
            update_phantoms_only=False,
            hide_phantoms_only=False,
            **kwargs):
        if update_phantoms_only:
            if self.show_errors_inline:
                self.update_phantoms()
            return

        if hide_phantoms_only:
            self.hide_phantoms()
            return

        # Try to handle killing
        with self.proc_lock:
            if self.proc:  # if we are running, try to kill running process
                self.output("\n\n### Got request to terminate compilation ###")
                try:
                    if sublime.platform() == 'windows':
                        execute_command('taskkill /t /f /pid {pid}'.format(
                            pid=self.proc.pid),
                                        use_texpath=False)
                    else:
                        os.killpg(self.proc.pid, signal.SIGTERM)
                except:
                    print('Exception occurred while killing build')
                    traceback.print_exc()

                self.proc = None
                return
            else:  # either it's the first time we run, or else we have no running processes
                self.proc = None

        view = self.view = self.window.active_view()

        if _HAS_PHANTOMS:
            self.hide_phantoms()
            pref_settings = sublime.load_settings(
                "Preferences.sublime-settings")
            self.show_errors_inline = pref_settings.get(
                "show_errors_inline", True)

        if view.is_dirty():
            print("saving...")
            view.run_command('save')  # call this on view, not self.window

        if view.file_name() is None:
            sublime.error_message(
                'Please save your file before attempting to build.')
            return

        self.file_name = getTeXRoot.get_tex_root(view)
        if not os.path.isfile(self.file_name):
            sublime.error_message(self.file_name + ": file not found.")
            return

        self.tex_base = get_jobname(view)
        self.tex_dir = os.path.dirname(self.file_name)

        if not is_tex_file(self.file_name):
            sublime.error_message(
                "%s is not a TeX source file: cannot compile." %
                (os.path.basename(view.file_name()), ))
            return

        # Output panel: from exec.py
        if not hasattr(self, 'output_view'):
            self.output_view = self.window.get_output_panel("latextools")

        output_view_settings = self.output_view.settings()
        output_view_settings.set("result_file_regex", file_regex)
        output_view_settings.set("result_base_dir", self.tex_dir)
        output_view_settings.set("line_numbers", False)
        output_view_settings.set("gutter", False)
        output_view_settings.set("scroll_past_end", False)

        if get_setting("highlight_build_panel", True, view=view):
            self.output_view.set_syntax_file(
                "Packages/LaTeXTools/LaTeXTools Console.hidden-tmLanguage")
            output_view_settings.set(
                "color_scheme",
                sublime.load_settings('Preferences.sublime-settings').get(
                    'color_scheme'))

        self.output_view.set_read_only(True)

        # Dumb, but required for the moment for the output panel to be picked
        # up as the result buffer
        self.window.get_output_panel("latextools")

        self.hide_panel_level = get_setting("hide_build_panel",
                                            "no_warnings",
                                            view=view)
        if self.hide_panel_level == "never":
            self.show_output_panel(force=True)

        self.plat = sublime.platform()
        if self.plat == "osx":
            self.encoding = "UTF-8"
        elif self.plat == "windows":
            self.encoding = getOEMCP()
        elif self.plat == "linux":
            self.encoding = "UTF-8"
        else:
            sublime.error_message("Platform as yet unsupported. Sorry!")
            return

        # Get platform settings, builder, and builder settings
        platform_settings = get_setting(self.plat, {}, view=view)
        self.display_bad_boxes = get_setting("display_bad_boxes",
                                             False,
                                             view=view)

        if builder is not None:
            builder_name = builder
        else:
            builder_name = get_setting("builder", "traditional", view=view)

        # Default to 'traditional' builder
        if builder_name in ['', 'default']:
            builder_name = 'traditional'

        # this is to convert old-style names (e.g. AReallyLongName)
        # to new style plugin names (a_really_long_name)
        builder_name = _classname_to_internal_name(builder_name)

        builder_settings = get_setting("builder_settings", {}, view=view)

        # override the command
        if command is not None:
            builder_settings.set("command", command)

        # parse root for any %!TEX directives
        tex_directives = parse_tex_directives(
            self.file_name,
            multi_values=['options'],
            key_maps={'ts-program': 'program'})

        # determine the engine
        if program is not None:
            engine = program
        else:
            engine = tex_directives.get(
                'program', builder_settings.get("program", "pdflatex"))

        engine = engine.lower()

        # Sanity check: if "strange" engine, default to pdflatex (silently...)
        if engine not in [
                'pdflatex', "pdftex", 'xelatex', 'xetex', 'lualatex', 'luatex'
        ]:
            engine = 'pdflatex'

        options = builder_settings.get("options", [])
        if isinstance(options, strbase):
            options = [options]

        if 'options' in tex_directives:
            options.extend(tex_directives['options'])

        # filter out --aux-directory and --output-directory options which are
        # handled separately
        options = [
            opt for opt in options
            if (not opt.startswith('--aux-directory') and not opt.startswith(
                '--output-directory') and not opt.startswith('--jobname'))
        ]

        self.aux_directory = get_aux_directory(view)
        self.output_directory = get_output_directory(view)

        # Read the env option (platform specific)
        builder_platform_settings = builder_settings.get(self.plat, {})

        if env is not None:
            self.env = env
        elif builder_platform_settings:
            self.env = builder_platform_settings.get("env", None)
        else:
            self.env = None

        # Safety check: if we are using a built-in builder, disregard
        # builder_path, even if it was specified in the pref file
        if builder_name in ['simple', 'traditional', 'script', 'basic']:
            builder_path = None
        else:
            # relative to ST packages dir!
            builder_path = get_setting("builder_path", "", view=view)

        if builder_path:
            bld_path = os.path.join(sublime.packages_path(), builder_path)
            add_plugin_path(bld_path)

        try:
            builder = get_plugin('{0}_builder'.format(builder_name))
        except NoSuchPluginException:
            try:
                builder = get_plugin(builder_name)
            except NoSuchPluginException:
                sublime.error_message(
                    "Cannot find builder {0}.\n"
                    "Check your LaTeXTools Preferences".format(builder_name))
                self.window.run_command('hide_panel',
                                        {"panel": "output.latextools"})
                return

        if builder_name == 'script' and script_commands:
            builder_platform_settings['script_commands'] = script_commands
            builder_settings[self.plat] = builder_platform_settings

        print(repr(builder))
        self.builder = builder(self.file_name, self.output, engine, options,
                               self.aux_directory, self.output_directory,
                               self.tex_base, tex_directives, builder_settings,
                               platform_settings)

        # Now get the tex binary path from prefs, change directory to
        # that of the tex root file, and run!
        if path is not None:
            self.path = path
        else:
            self.path = get_texpath() or os.environ['PATH']

        thread = CmdThread(self)
        thread.start()
        print(threading.active_count())

        # setup the progress indicator
        display_message_length = long(
            get_setting('build_finished_message_length', 2.0, view=view) *
            1000)
        # NB CmdThread will change the success message
        self.progress_indicator = ProgressIndicator(
            thread,
            'Building',
            'Build failed',
            display_message_length=display_message_length)
Exemple #53
0
 def is_enabled(self):
     return get_setting('ref_auto_trigger', True)
    def run(self, cmd="", file_regex="", path=""):

        # Try to handle killing
        with self.proc_lock:
            if self.proc:  # if we are running, try to kill running process
                self.output("\n\n### Got request to terminate compilation ###")
                if sublime.platform() == 'windows':
                    startupinfo = subprocess.STARTUPINFO()
                    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                    subprocess.call(
                        'taskkill /t /f /pid {pid}'.format(pid=self.proc.pid),
                        startupinfo=startupinfo,
                        shell=True)
                else:
                    os.killpg(self.proc.pid, signal.SIGTERM)
                self.proc = None
                return
            else:  # either it's the first time we run, or else we have no running processes
                self.proc = None

        view = self.view = self.window.active_view()

        if view.is_dirty():
            print("saving...")
            view.run_command('save')  # call this on view, not self.window

        if view.file_name() is None:
            sublime.error_message(
                'Please save your file before attempting to build.')
            return

        self.file_name = getTeXRoot.get_tex_root(view)
        if not os.path.isfile(self.file_name):
            sublime.error_message(self.file_name + ": file not found.")
            return

        self.tex_base = get_jobname(view)
        self.tex_dir = os.path.dirname(self.file_name)

        if not is_tex_file(self.file_name):
            sublime.error_message(
                "%s is not a TeX source file: cannot compile." %
                (os.path.basename(view.file_name()), ))
            return

        # Output panel: from exec.py
        if not hasattr(self, 'output_view'):
            self.output_view = self.window.get_output_panel("latextools")

        output_view_settings = self.output_view.settings()
        output_view_settings.set("result_file_regex", file_regex)
        output_view_settings.set("result_base_dir", self.tex_dir)
        output_view_settings.set("line_numbers", False)
        output_view_settings.set("gutter", False)
        output_view_settings.set("scroll_past_end", False)

        if get_setting("highlight_build_panel", True):
            self.output_view.set_syntax_file(
                "Packages/LaTeXTools/LaTeXTools Console.hidden-tmLanguage")
            output_view_settings.set(
                "color_scheme",
                sublime.load_settings('Preferences.sublime-settings').get(
                    'color_scheme'))

        self.output_view.set_read_only(True)

        # Dumb, but required for the moment for the output panel to be picked
        # up as the result buffer
        self.window.get_output_panel("latextools")

        self.hide_panel_level = get_setting("hide_build_panel", "never")
        if self.hide_panel_level != "always":
            self.window.run_command("show_panel",
                                    {"panel": "output.latextools"})

        self.plat = sublime.platform()
        if self.plat == "osx":
            self.encoding = "UTF-8"
        elif self.plat == "windows":
            self.encoding = getOEMCP()
        elif self.plat == "linux":
            self.encoding = "UTF-8"
        else:
            sublime.error_message("Platform as yet unsupported. Sorry!")
            return

        # Get platform settings, builder, and builder settings
        platform_settings = get_setting(self.plat, {})
        builder_name = get_setting("builder", "traditional")
        self.display_bad_boxes = get_setting("display_bad_boxes", False)
        # This *must* exist, so if it doesn't, the user didn't migrate
        if builder_name is None:
            sublime.error_message(
                "LaTeXTools: you need to migrate your preferences. See the README file for instructions."
            )
            self.window.run_command('hide_panel',
                                    {"panel": "output.latextools"})
            return

        # Default to 'traditional' builder
        if builder_name in ['', 'default']:
            builder_name = 'traditional'

        # this is to convert old-style names (e.g. AReallyLongName)
        # to new style plugin names (a_really_long_name)
        builder_name = _classname_to_internal_name(builder_name)

        builder_settings = get_setting("builder_settings", {})

        # parse root for any %!TEX directives
        tex_directives = parse_tex_directives(
            self.file_name,
            multi_values=['options'],
            key_maps={'ts-program': 'program'})

        # determine the engine
        engine = tex_directives.get(
            'program', builder_settings.get("program", "pdflatex"))

        engine = engine.lower()

        # Sanity check: if "strange" engine, default to pdflatex (silently...)
        if engine not in [
                'pdflatex', "pdftex", 'xelatex', 'xetex', 'lualatex', 'luatex'
        ]:
            engine = 'pdflatex'

        options = builder_settings.get("options", [])
        if isinstance(options, strbase):
            options = [options]

        if 'options' in tex_directives:
            options.extend(tex_directives['options'])

        # filter out --aux-directory and --output-directory options which are
        # handled separately
        options = [
            opt for opt in options
            if (not opt.startswith('--aux-directory') and not opt.startswith(
                '--output-directory') and not opt.startswith('--jobname'))
        ]

        self.aux_directory = get_aux_directory(self.file_name)
        self.output_directory = get_output_directory(self.file_name)

        # Read the env option (platform specific)
        builder_platform_settings = builder_settings.get(self.plat)
        if builder_platform_settings:
            self.env = builder_platform_settings.get("env", None)
        else:
            self.env = None

        # Now actually get the builder
        builder_path = get_setting("builder_path",
                                   "")  # relative to ST packages dir!

        # Safety check: if we are using a built-in builder, disregard
        # builder_path, even if it was specified in the pref file
        if builder_name in ['simple', 'traditional', 'script', 'basic']:
            builder_path = None

        if builder_path:
            bld_path = os.path.join(sublime.packages_path(), builder_path)
            add_plugin_path(bld_path)

        try:
            builder = get_plugin('{0}_builder'.format(builder_name))
        except NoSuchPluginException:
            sublime.error_message("Cannot find builder " + builder_name + ".\n" \
                      "Check your LaTeXTools Preferences")
            self.window.run_command('hide_panel',
                                    {"panel": "output.latextools"})
            return

        print(repr(builder))
        self.builder = builder(self.file_name, self.output, engine, options,
                               self.aux_directory, self.output_directory,
                               self.tex_base, tex_directives, builder_settings,
                               platform_settings)

        # Now get the tex binary path from prefs, change directory to
        # that of the tex root file, and run!
        self.path = platform_settings['texpath']
        CmdThread(self).start()
        print(threading.active_count())
Exemple #55
0
    def run(self, edit, insert_char=""):
        view = self.view
        point = view.sel()[0].b
        # Only trigger within LaTeX
        # Note using score_selector rather than match_selector
        if not view.score_selector(point, "text.tex.latex"):
            return

        if not is_cwl_available():
            if insert_char:
                view.insert(edit, point, insert_char)
                add_closing_bracket(view, edit)
            return

        if insert_char:
            # append the insert_char to the end of the current line if it
            # is given so this works when being triggered by pressing "{"
            point += view.insert(edit, point, insert_char)

            do_completion = get_setting("env_auto_trigger", False)

            if not do_completion:
                add_closing_bracket(view, edit)
                return

        if not insert_char:
            # only use the prefix if all cursors have the same
            prefix = get_current_word(view, point)[0]
            for sel in view.sel():
                other_prefix = get_current_word(view, sel.b)[0]
                if other_prefix != prefix:
                    prefix = ""
                    break
        else:
            prefix = ""

        completions = parse_cwl_file(parse_line_as_environment)
        if prefix:
            completions = [c for c in completions if c[1].startswith(prefix)]

        show_entries = [c[0].split("\t") for c in completions]

        def on_done(index):
            if index < 0:
                return
            key = completions[index][1]
            # close bracket
            if insert_char:
                key += "}"

            if prefix:
                for sel in view.sel():
                    point = sel.b
                    startpoint = point - len(prefix)
                    endpoint = point
                    view.run_command('latex_tools_replace', {
                        'a': startpoint,
                        'b': endpoint,
                        'replacement': key
                    })
            else:
                view.run_command("insert", {"characters": key})

        # autocomplete bracket if we aren't doing anything
        if not show_entries and insert_char:
            add_closing_bracket(view, edit)
        else:
            view.window().show_quick_panel(show_entries, on_done)
Exemple #56
0
 def on_query_context(self, view, key, *args):
     if (key != "overwrite_goto_overlay"
             or not view.score_selector(0, "text.tex.latex")):
         return None
     return get_setting("overwrite_goto_overlay")
Exemple #57
0
 def is_enabled(self):
     return get_setting("tex_directive_auto_trigger", True)
    def run(self):
        texpath = self.texpath
        results = []

        env = copy.deepcopy(os.environ)

        if texpath is not None:
            env['PATH'] = texpath
        if self.build_env is not None:
            update_environment(env, self.build_env)

        table = [['Variable', 'Value']]

        table.append(['PATH', env.get('PATH', '')])

        if self.uses_miktex:
            get_tex_path_variable = get_tex_path_variable_miktex
            should_run = which('findtexmf', path=texpath) is not None
        else:
            get_tex_path_variable = get_tex_path_variable_texlive
            should_run = which('kpsewhich', path=texpath) is not None

        if should_run:
            for var in ['TEXINPUTS', 'BIBINPUTS', 'BSTINPUTS']:
                table.append([var, get_tex_path_variable(var, env)])

        if self.uses_miktex:
            for var in [
                    'BIBTEX', 'LATEX', 'PDFLATEX', 'MAKEINDEX', 'MAKEINFO',
                    'TEX', 'PDFTEX', 'TEXINDEX'
            ]:
                value = env.get(var, None)
                if value is not None:
                    table.append([var, value])

        results.append(table)

        table = [['Program', 'Location', 'Status', 'Version']]

        # skip sublime_exe on OS X
        # we only use this for the hack to re-focus on ST
        # which doesn't work on OS X anyway
        if sublime.platform() != 'osx':
            sublime_exe = self.sublime_exe
            available = sublime_exe is not None

            if available:
                if not os.path.isabs(sublime_exe):
                    sublime_exe = which(sublime_exe)

                basename, extension = os.path.splitext(sublime_exe)
                if extension is not None:
                    sublime_exe = ''.join((basename, extension.lower()))

            version_info = get_version_info(sublime_exe,
                                            env=env) if available else None

            table.append([
                'sublime', sublime_exe,
                (u'available'
                 if available and version_info is not None else u'missing'),
                version_info if version_info is not None else u'unavailable'
            ])

        # a list of programs, each program is either a string or a list
        # of alternatives (e.g. 32/64 bit version)
        programs = [
            'latexmk' if not self.uses_miktex else 'texify', 'pdflatex',
            'xelatex', 'lualatex', 'biber', 'bibtex', 'bibtex8', 'kpsewhich',
            ('gs' if sublime.platform() != 'windows' else
             ['gswin32c', 'gswin64c', 'gs'])
        ]

        if _HAS_PREVIEW:
            # ImageMagick requires gs to work with PDFs
            programs += [['magick', 'convert']]

        for program in programs:
            if isinstance(program, list):
                program_list = program
                program = program_list[0]
                location = None
                for p in program_list:
                    location = which(p, path=texpath)
                    if location is not None:
                        program = p
                        break
            else:
                location = which(program, path=texpath)

            available = location is not None

            if available:
                basename, extension = os.path.splitext(location)
                if extension is not None:
                    location = ''.join((basename, extension.lower()))

            version_info = get_version_info(location,
                                            env=env) if available else None

            available_str = (u'available' if available
                             and version_info is not None else u'missing')

            if (available and program in ['magick', 'convert']
                    and not convert_installed()):
                available_str = u'restart required'

            table.append([
                program, location, available_str,
                version_info if version_info is not None else u'unavailable'
            ])

        results.append(table)

        # This really only works for the default template
        # Note that no attempt is made to find other packages that the
        # included package depends on
        if (_HAS_PREVIEW and convert_installed()
                and get_setting('preview_math_template_file') is None and
                get_setting("preview_math_mode", view=self.view) != "none"):

            find_package_re = re.compile(
                r'\\usepackage(?:\[[^\]]*\])?\{(?P<pkg>[^\}]*)\}')

            packages = ["standalone.cls", "preview.sty", "xcolor.sty"]

            package_settings = get_setting("preview_math_template_packages",
                                           [],
                                           view=self.view)
            # extract all packages from each package line
            for pkg_str in package_settings:
                # search for all \usepackage in the line
                for m in find_package_re.finditer(pkg_str):
                    pkg_arg = m.group("pkg")
                    # search for each package in the \usepackage argument
                    for pkg in pkg_arg.split(","):
                        pkg = pkg.strip()
                        if pkg:
                            packages.append(pkg + ".sty")

            if packages:
                table = [[u'Packages for equation preview', u'Status']]

                for package in packages:
                    available = kpsewhich(package) is not None
                    package_name = package.split(".")[0]
                    table.append([
                        package_name,
                        (u'available' if available else u'missing')
                    ])

                results.append(table)

        run_on_main_thread(partial(self._on_main_thread, results), timeout=30)
Exemple #59
0
def using_texify_or_simple():
    if using_miktex():
        builder = get_setting('builder', 'traditional')
        if builder in ['', 'default', 'traditional', 'simple']:
            return True
    return False
    def get_closing_bracket(self, view, sel):
        '''
        Determines if the nearest bracket that occurs before the given
        selection is closed. If the bracket should be closed, returns the
        closing bracket to use.

        Note that this will not work if the arguments to the command span
        multiple lines, but we generally don't support that anyway.

        :param view:
            the current view

        :param sel:
            a sublime.Region indicating the selected area
        '''
        # candidates stores matched bracket-pairs so that we only have
        # to find all matches once per bracket type
        # if the view has changed, we reset the candidates
        candidates = None

        if not hasattr(self, 'last_view') or self.last_view != view.id():
            self.last_view = view.id()
            self.use_full_scan = get_setting(
                'smart_bracket_scan_full_document', False)
            candidates = self.candidates = {}

        if not self.use_full_scan:
            # always clear the candidates when not using a full scan
            candidates = {}
            # when not using a full scan, get the number of lines to
            # look-behind
            try:
                look_around = int(get_setting('smart_bracket_look_around', 5))
            except ValueError:
                look_around = 5

        if candidates is None:
            try:
                candidates = self.candidates
            except:
                candidates = self.candidates = {}

        # first, find the nearest bracket
        if type(sel) is sublime.Region:
            start, end = sel.begin(), sel.end()
        else:
            start = end = sel

        if self.use_full_scan:
            prefix = view.substr(getRegion(0, start))
            prefix_start = 0
            suffix_end = view.size()
        else:
            prefix_lines = view.lines(getRegion(0, start))
            if len(prefix_lines) >= look_around:
                prefix_start = prefix_lines[-look_around].begin()
            else:
                prefix_start = prefix_lines[0].begin()

            suffix_lines = view.lines(getRegion(end, view.size()))
            if len(suffix_lines) >= look_around:
                suffix_end = suffix_lines[look_around - 1].end()
            else:
                suffix_end = suffix_lines[-1].end()

            prefix = view.substr(getRegion(prefix_start, start))

        open_bracket, last_index = None, -1
        for char in self.MATCH_CHARS:
            index = prefix.rfind(char)
            if index > last_index:
                open_bracket, last_index = char, index

        if last_index == -1:
            # can't determine bracket to match
            return None

        close_bracket = self.MATCH_CHARS[open_bracket]
        open_brackets = []
        # this is used to throw-away as many matches as possible
        # so subsequent requests don't need to consider every match
        closed_brackets = []

        if open_bracket not in candidates:
            # find all open / close brackets in the current buffer,
            # removing all comments
            candidates[open_bracket] = results = []

            start = prefix_start
            re_str = re.escape(open_bracket) + '|' + re.escape(close_bracket)
            while True:
                if start >= suffix_end:
                    break

                c = view.find(re_str, start)
                if c is None or c.begin() == -1:
                    break

                if c.end() > suffix_end:
                    break

                if view.score_selector(c.begin(), 'comment') != 0:
                    start = c.end()
                    continue

                results.append(c)

                start = c.end()

        for candidate in candidates[open_bracket]:
            if view.substr(candidate) == open_bracket:
                if len(open_brackets) == 0 and candidate.begin() > end:
                    break

                open_brackets.append(candidate)
            else:
                try:
                    removed = open_brackets.pop()
                    if candidate.end() < start:
                        closed_brackets.append(removed)
                        closed_brackets.append(candidate)
                except IndexError:
                    # unbalanced close before open
                    if candidate.end() > end:
                        break

        if len(closed_brackets) > 0:
            candidates[open_bracket] = [
                c for c in candidates[open_bracket] if c not in closed_brackets
            ]

        # if we have an open bracket left, then the current bracket needs to
        # be closed
        return close_bracket if len(open_brackets) > 0 else None