[TxMt] Re: Latex project opens in new window (not new tab)

Gildas Hamel gweltaz at ucsc.edu
Thu Apr 19 23:40:40 UTC 2012


* Skriv a reas David Howden (dhowden at gmail.com):
  |>  >
  |>  >  Thank you for the link and the improvements. Because I use biblatex and
  |>  > need to parse my bibdesk datafiles while writing, I had to add line 244 and
  |>  > change line 248 in LaTeXUtils.rb in Support/lib. Screenshot attached.
  |>  >
  |>  
  |>  it would be easier if you could just attach the new files, then I can check
  |>  them in!
  
  Ok, fair enough, attached. But I don't program and I worry about how to make the change to line 248 so that both [biblio_regexp] and [addbib_regexp] work (for regular bibtex as well as biblatex).
  --Gildas
-------------- next part --------------
require 'strscan'
require 'pathname'
# The LaTeX module contains a lot of methods useful when dealing with LaTeX
# files.
#
# Author:: Charilaos Skiadas
# Date:: 12/21/2006
module LaTeX
  # Simple conversion of bib field names to a simpler form
  def e_var(name)
    name.gsub(/[^a-zA-Z0-9\-_]/,"").gsub(/\-/,"_")
  end
  # parse any %!TEX options in the first 20 lines of the file
  # Only use the first 20 lines for compatibility with TeXShop
  # returns a hash of the results
  def self.options(filepath)
    opts = {}
    begin
      File.open(filepath, "r") do |file|
        1.upto(20) do
          line = file.readline
          if line =~ /^%!TEX (\S*) =\s*(.*)\s*$/
            opts[$1] = $2
          end
        end
      end
    rescue EOFError
      # Don't do anything
    end
    opts
  end
  
  # Returns the root file for the given filepath
  # If no master exists, return the given filepath
  # Stop searching after 10 iterations, in case of loop
  def self.master(filepath)
    return nil if filepath.nil?
    master = Pathname.new(filepath).cleanpath
    opts = options(master)
    iter = 0
    while opts.has_key?('root') and iter < 10
      new_master = (master.parent + Pathname.new(opts['root'])).cleanpath
      break if new_master == master
      master = new_master
      opts = options(master)
      iter += 1
    end
    master.to_s
  end
  # Implements general methods that give information about the LaTeX document. 
  # Most of these commands recurse into \included files.
  class <<self
    # Returns an array of the label names. If you want actual Label objects, 
    # then use FileScanner.label_scan
    def get_labels
      mFile = LaTeX.master(ENV["TM_LATEX_MASTER"] || ENV["TM_FILEPATH"])
      return FileScanner.label_scan(mFile).map{|i| i.label}.sort
    end
    # Returns an array of the citation objects. If you only want the citekeys, 
    # use LaTeX.get_citekeys
    def get_citations
      mFile = LaTeX.master(ENV["TM_LATEX_MASTER"] || ENV["TM_FILEPATH"])
      return FileScanner.cite_scan(mFile).map{|i| i}.sort { |a,b| a.citekey <=> b.citekey }
    end
    # Returns an array of the citekeys in the document.
    def get_citekeys
      self.get_citations.map{|i| i["citekey"]}.uniq
    end
    # Returns the path to the TeX binaries, or raises an exception if it can't find them.
    def tex_path
      # First try directly
      return "" if ENV['PATH'].split(':').find { |dir| File.exists? File.join(dir, 'kpsewhich') }
      # Then try some specific paths
      locs = ["/usr/texbin/",
              "/usr/local/teTeX/bin/powerpc-apple-darwin-current/",
              "/usr/local/teTeX/bin/i386-darwin/",
              "/usr/local/teTeX/bin/i386-apple-darwin-current/",
              "/opt/local/bin/"]
      locs.each do |loc|
        return loc if File.exist?(loc+"kpsewhich")
      end
#     If all else fails, rely on /etc/profile. For most people, we should never make it here.
      loc = `. /etc/profile; which kpsewhich`
      return loc.gsub(/kpsewhich$/,"") unless loc.match(/^no kpsewhich/)
      raise "The tex binaries cannot be located!"
    end
    # Uses kpsewhich to locate the file with name +filename+ and +extension+.
    # +relative+ determines an explicit path that should be included in the
    # paths to look at when searching for the file. This will typically be the
    # path to the root document.
    
    # TODO: The following method should probably be simplified dramatically
    def find_file(filename, extension, relative)
      filename.gsub!(/"/,"")
      filename.gsub!(/\.#{extension}$/,"")
      return filename if File.exist?(filename) && !File.directory?(filename)  # First try the filename as is, without the extension
      return "#{filename}.#{extension}" if File.exist?("#{filename}.#{extension}") # Then try with the added extension
      return nil if filename.match(/^\//) # If it is an absolute path, and the above two tests didn't find it, return nil
      texpath = LaTeX.tex_path
      @@paths ||= Hash.new
      @@paths[extension] ||= ([`#{texpath}kpsewhich -show-path=#{extension}`.chomp.split(/:!!|:/)].flatten.map{|i| i.sub(/\/*$/,'/')}).unshift(relative).unshift("")
      @@paths[extension].each do |path|
        testpath = File.expand_path(File.join(path,filename))
        return testpath if File.exist?(testpath)
        testpath = File.expand_path(File.join(path,filename + "." + extension))
        return testpath if File.exist?(testpath)
      end
      return nil
    end
    # Processes the .bib file with title +file+, and returns an array of the
    # Citation objects.
    def parse_bibfile(file)
      raise "Could not locate file: #{file.to_s}" if (file.nil? or !File.exist?(file))
      text = File.read(file)
      entries = text.scan(/^\s*@[^\{]*\{.*?(?=\n[ \t]*@|\z)/m)
      citations = entries.map do |text|
        c = Citation.new
        s = StringScanner.new(text)
        s.scan(/\s+@/)
        c["bibtype"] = s.scan(/[^\s\{]+/)
        s.scan(/\s*\{\s*/)
        c["citekey"] = s.scan(/[^\s,]+(?=\s*,)/)
        if c["citekey"].nil?
          c = nil
          next
        end
        # puts "Found citekey: #{c["citekey"]}"
        s.scan(/\s*,/)
        until s.eos? or s.scan(/\s*\,?\s*\}/) do
          s.scan(/\s+/)
          key = s.scan(/[\w\-\.]+/)
          unless s.scan(/\s*=\s*/) then
            s.scan(/[^@]*/m)
            c=nil
            next
          end
          # puts "Found key: #{key}"
          contents = ""
          nest_level = 0
          until nest_level <= 0 and s.check(/[\},]/) do
            contents << (s.scan(/[^\{\}#{if nest_level == 0 then "," else "" end}]+/) || "")
            if s.scan(/\{/) then
              contents << "\{" unless nest_level == 0
              nest_level += 1
            elsif s.check(/\}/) then
              nest_level -= 1
              contents << "\}" unless nest_level <= 0
              s.scan(/\}/) unless nest_level == -1
            end
          end
          c[key] = contents
          # puts "Found contents: #{contents}"
          raise "Unexpected end of bib entry" unless s.scan(/\s*(\,|\})\s*/)
        end
        c
      end
      return citations.compact
    end
  end
  # A class implementing a recursive scanner.
  # +root+ is the file to start the scanning from.
  # +includes+ is a hash with keys regular expressions and values blocks of
  #   code called when that expression matches. The block is passed the
  #   matched groups in the kind of array returned by String#scan as argument,
  #   and must return the full path to the file to be recursively scanned.
  # +extractors+ is a similar hash, dealing with the bits of text to be
  #   matched. The block is passed as arguments the current filename, the
  #   current line number counting from 0, the matched groups in the kind
  #   of array returned by String#scan and finally the entire file contents.
  class FileScanner
    attr_accessor :root, :includes, :extractors
    # Creates a new scanner object. If the argument +old_scanner+ is a String,
    # then it is set as the +root+ file. Otherwise, it is used to read the
    # values of the three variables.
    def initialize(old_scanner=nil)
      if old_scanner then
        if old_scanner.is_a?(String) then
          @root = old_scanner
          self.set_defaults
        else
          @root = old_scanner.root
          @includes = old_scanner.includes
          @extractors = old_scanner.extractors
        end
      else
        self.set_defaults
      end
    end
    # Default values for the +includes+ hash.
    def set_defaults
      @includes = Hash.new
      @includes[/^[^%]*(?:\\include|\\input)\s*\{([^\}]*)\}/] = Proc.new {|m|
        m[0].split(",").map do |it|
          LaTeX.find_file( it.strip, "tex", File.dirname(@root) ) || raise("Could not locate any file named '#{it}'")
        end
      }
      @extractors = Hash.new
    end
    # Performs the recursive scanning.
    def recursive_scan
       raise "No root specified!" if @root.nil?
       raise "Could not find file #{@root}" unless File.exist?(@root)
       text = File.read(@root)
       text.each_with_index do |line, index|
         includes.each_pair do |regexp, block|
           line.scan(regexp).each do |m|
             newfiles = block.call(m)
             newfiles.each do |newfile|
               scanner = FileScanner.new(self)
               scanner.root = newfile.to_s
               scanner.recursive_scan
             end
           end
           extractors.each_pair { |regexp,block| 
             line.scan(regexp).each do |m|
               block.call(root,index,m,text)
             end
            }
         end
       end
    end
    # Creates a FileScanner object and uses it to read all the labels from the
    # document. Returns a list of Label objects.
    def self.label_scan(root)
      # LaTeX.set_paths
      labelsList = Array.new
      scanner = FileScanner.new(root)
      scanner.extractors[/.*?\[.*label=(.*?)\,.*\]/] = Proc.new do |filename, line, groups, text| 
        labelsList << Label.new(:file => filename, :line => line, :label => groups[0], :contents => text)
      end
      scanner.extractors[/^[^%]*\\label\{([^\}]*)\}/] = Proc.new do |filename, line, groups, text| 
        labelsList << Label.new(:file => filename, :line => line, :label => groups[0], :contents => text)
      end
      scanner.recursive_scan
      labelsList
    end
    # Creates a FileScanner object and uses it to read all the citations from
    # the document. Returns a list of Citation objects.
    def self.cite_scan(root)
      citationsList = Array.new
      scanner = FileScanner.new(root)
      bibitem_regexp = /^[^%]*\\bibitem(?:\[[^\]]*\])?\{([^\}]*)\}(.*)/
      biblio_regexp = /^[^%]*\\bibliography\s*\{([^\}]*)\}/
      addbib_regexp = /^[^%]*\\addbibresource\s*\{([^\}]*)\}/
      scanner.extractors[bibitem_regexp] = Proc.new do |filename, line, groups, text|
      citationsList << Citation.new( "citekey" => groups[0], "cite_data" => groups[1])
      end
      scanner.extractors[addbib_regexp] = Proc.new do |filename, line, groups, text|
        groups[0].split(",").each do |it|
          file = LaTeX.find_file( it.strip, "bib", File.dirname(root) )
          raise "Could not locate any file named '#{it}'" if file.nil?
          citationsList += LaTeX.parse_bibfile(file)
          citationsList += LaTeX.parse_bibfile(ENV["TM_LATEX_BIB"]) unless ENV["TM_LATEX_BIB"].nil?
        end
      end
      scanner.recursive_scan
      citationsList
    end
  end
  class Label
    attr_accessor :file, :line, :label, :contents
    def initialize(hash)
      ["file","line","label","contents"].each do |key|
        eval("@#{key} = hash[:#{key}]")
      end
    end
    def to_s
      label
    end
    # Returns the text around the label.
    def context(chars = 40, countlines=false)
      if countlines then
        return contents.match(/(.*\n){#{chars/2}}.*\\label\{#{label}\}.*\n(.*\n){#{chars/2}}/)
      else
        return contents.gsub(/\s/,"").match(/.{#{chars/2}}\\label\{#{label}\}.{#{chars/2}}/)
      end
    end
    def file_line_label
      "#{file}:#{line}:#{label}"
    end
  end
  class Citation
    def initialize(hash = Hash.new)
      @hash = Hash.new
      hash.each_pair do |key,value|
        @hash[key.downcase] = value
      end
    end
    def []=(key,value)
      @hash[key.downcase] = value
    end
    def [](key)
      @hash[key.downcase]
    end
    def author
      @hash["author"] || @hash["editor"]
    end
    def title
      @hash["title"]
    end
    def description
      @hash["cite_data"] || "#{self.author}, #{self.title}"
    end
    def citekey
      @hash["citekey"]
    end
  end
end
# Example of use:
#
# include LaTeX
# # ar = FileScanner.cite_scan("/Users/haris/svnlocalrepos/repos/master.tex")
# ar = FileScanner.cite_scan("/Users/haris/Desktop/testing/Morten/test2.tex")
# puts ar.length
# ar.each do |citation|
#   puts citation.description
# end


More information about the textmate mailing list