Showing posts with label TeXShop. Show all posts
Showing posts with label TeXShop. Show all posts

Monday, May 24, 2010

LaTeX: Listings and labels

I have blogged about the listing package here several times (nicely formatting, and hyphenations). This time I will write about creating labels inside a listing. First of all, the listing package allows to define a caption and a label for a listing, e.g.:
\begin{lstlisting}[caption={myListing},label={lst:myListing}]
void foo(int x) {
 doSomething();
 doMore();
}
\end{lstlisting}
Now the listing can be referenced via \ref{lst:myListing}. The listing package also nicely supports line numbers, there are a whole bunch of settings for that. But why do we need line numbers? In most cases, line numbers are used to refer to a certain line within a listing. E.g, we maybe want to write something like
In line 2 of the listing 1 we call method 'doSomething'.
Well, "listing 1" will look like "listing \ref{lst:myListing}" in the LaTeX source code, but how do we reference the line? Fortunately, the listing packages allows us to escape to latex inside a listing and add a label which can then be referenced:
\begin{lstlisting}[caption={myListing},label={lst:myListing},numbers=left,escapeinside={@}{@}]
void foo(int x) {
 @\label{lst:myListing_2}@doSomething();
 doMore();
}
\end{lstlisting}
Now we can reference the line: In line \ref{lst:myListing_2} ... Actually, you can use any label text, however I usually use the listings label with the line number or a small marker, in order to avoid conflicts with duplicate labels. As I do not want to write "line..." and "listing ..." over and over again, I use the autoref command from the hyperref package:
In \autoref{lst:myListing_2} of \autoref{lst:myListing}
autoref automatically adds a name to the reference counter. As there is no name defined for line numbers, it has to be defined previously:
\providecommand*{\lstnumberautorefname}{line} 
If you need names in a another language, you can add a translation:
\addto\extrasngerman{% 
\def\lstlistingautorefname{Quellcode}% 
\def\lstnumberautorefname{Zeile}% 
}
OK, we can add a line label and easily create a reference to that label. However, writing line labels means a lot of work, especially if you use a listing relative name pattern. So, let's add a macro to TeXShop, which uses some shell commands (mainly sed) and AppleScript in order to automatically create the label, even adding the line number to the label:
--Applescript direct
--
-- Create a label inside a listing, must be invoked at the position at which the label is
-- to be created.
-- Precondition: the listing is defined inside a lstlisting environment and 
--   a label is defined in the parameter list of the environment (label={..}).
--
-- New newly created label consists of the label of the listing with the line number
-- appended to the label. You can configure escape characters (as specified in
-- escapeinside={..}{..}) and the separator between listing label and line number.
--
-- E.g. label={myListing}, on line 5 leads to the following output:
-- @\label{lst: myListing!5}@
--
-- (C) 2010 Jens von Pilgrim, http://jevopi.blogspot.com

property texapp : "TeXShop"
property escapeLeft : "@"
property escapeRight : "@"
property separator : "_"
property maxLength : 2000

try
 tell application texapp
  
  if texapp = "TeXShop" then
   tell application "TeXShop" to set pos to the offset of the selection of the front document
   tell application "TeXShop" to set currentSelection to the content of the selection of the front document
  else if texapp = "iTeXMac" then
   -- ??
  end if
  
  set start to 1
  if (pos > maxLength) then
   set start to pos - maxLength
  end if
  
  set preceeding_text to (characters start thru pos of (the text of the front document)) as string
  set preceeding_text to my findAndReplace(preceeding_text, "\\", "_")
  
  set lineNumbers to do shell script ¬
   "echo " & the quoted form of preceeding_text & ¬
   "| sed -n '/label[:space:]*=[:space:]*{[^}]*}/=;$ {x;=;}'"
  
  
  
  set theLines to my splitLines(lineNumbers) -- as list
  set currentLine to last item of theLines as integer
  -- set startLine to item ((length of theLines) - 1) of theLines as integer
  -- length is not working when executed as macro... workaround:
  set startLine to first item of (rest of (reverse of theLines))
  
  set lstLine to currentLine - startLine
  
  set lastLabel to do shell script ¬
   "echo " & the quoted form of preceeding_text & ¬
   "| sed -n '/label[:space:]*=[:space:]*{[^}]*}/h;$ {x;p;}'" & ¬
   "| sed -n 's/.*label[:space:]*=[:space:]*{\\([^}]*\\)}.*/\\1/g;p'"
  
  set newSelection to escapeLeft & "\\label{" & lastLabel & separator & lstLine & "}" & escapeRight & currentSelection
  
  
  if texapp = "TeXShop" then
   tell application "TeXShop" to set the selection of the front document to newSelection
  else if texapp = "iTeXMac" then
   --tell application "iTeXMac" to insert new_section in the text of the front document
  end if
  
 end tell
on error errmesg number errn
 beep
 display dialog errmesg
 return
end try


on findAndReplace(strInString, strFind, strReplace)
 set ditd to text item delimiters
 set text item delimiters to strFind
 set textItems to text items of strInString
 set text item delimiters to strReplace
 if (class of strInString is string) then
  set res to textItems as string
 else -- if (class of TheString is Unicode text) then
  set res to textItems as Unicode text
 end if
 set text item delimiters to ditd
 return res
end findAndReplace


on splitLines(strInString)
 set ditd to text item delimiters
 set text item delimiters to "
"
 set textItems to text items of strInString
 set text item delimiters to ditd
 return textItems
end splitLines
Now things are really easy: Simply activate the macro at the appropriate location in the listing, and a new label will automatically be created. Note: The listing must have a label defined! You can configure the script by changing the properties at the beginning of the macro.

Sunday, April 18, 2010

Convert Text to a (TeXShop) Convenient Label

TeXShop support smart selection of words. If you double-click a text, the whole word is selected. A word in TeXShop consists of the letters 'A/a' to 'Z/z' and underscores. Spaces and hyphen '-' are not interpreted as word but as whitespaces separating words. With BibDesk installed 1), you can press the F5 key within a \ref-command and a list will pop up with all the labels defined in your text.
If you want to quickly select a label in order to copy and paste it manually to a reference (e.g., if you do not use \ref but \autoref instead, the latter is not recognized as reference command by BibDesk), spaces and hyphens (and other punctuation characters) are really annoying as you cannot simply select the label using a double click. Even more annoying: If a label is selected from the proposal list (activiated via F5), only the label characters before the first space or punctuation character are pasted.
For that reason I do not use whitespaces or punctuation characters in labels. Since I do not want to convert all characters myself, I have added a new macro to TeXShop (with a nice shortkey on Cmd+'_') which does the trick:
--Applescript direct

-- Converts spaces and punctuation characters to underscores, useable for labels
-- (C) 2010 Jens von Pilgrim
tell application "TeXShop"
set snippet to the content of the selection of the front document
-- replace commands
set snippet to do shell script ¬
"echo " & the quoted form of snippet & ¬
" | sed -E 's/([[:space:]]|[[:punct:]])/_/g'"
set the selection of the front document to snippet
end tell
Note: In a previous posting, I published a macro creating section headers automatically. I have improved this macro in order to convert the whitespaces and punctuation characters in labels as well.
1)Thank you, Herb Schulz, for reminding me that it's BibDesk which adds the completion list to TeXShop ;-)

Tuesday, March 2, 2010

Create Section Headers with TexShop and Applescript

I really like the macro feature of TexShop. One of the most common use cases of macros would probably be the definition of a section (or subsection etc.). My section-creation-macro has been quite simple in the past:

% ----------------------------------------
% \section{#SEL##INS#}
% \label{sec:#SEL#}
% ----------------------------------------
This small snippet creates a nicely formatted section header including a label. However, there's a really annoying problem: When creating a reference to a section (that is, to its label), TeXShop does not handle spaces accordingly when selecting a label from the dropdown list. For example, if your label is something like \label{sec:Hello World}, the complete label is shown in the list, but only \ref{sec:Hello} is inserted into the text. In order to overcome this problem, I always replace spaces in labels with underscores. So my label would be "sec:Hello_World", and TeXShop is working perfectly. But replacing spaces with underscores by hand is even more annoying. So I replaced my section macro with an applescript version:

--Applescript direct

-- Create a section header from selected text. A label is automatically added with a prefix
-- "sec:", spaces are converted to underscores.
-- (C) 2010 Jens von Pilgrim

property level : "section"
property cmtchar : "-"
property cmtlength : 78
property labelprefix : "sec:"
-- property newline: "\n"
tell application "TeXShop"
 
 
 tell application "TeXShop" to set title to the content of the selection of the front document
 
 set new_title to title
 if ((count of the paragraphs of title) ≥ 1) then
  set this_line to paragraph 1 of title
  set label to this_line
  set add to "true"
  
  -- replace commands
  set label to do shell script ¬
   "echo " & the quoted form of label & ¬
   " | sed -E 's/([[:space:]]|[[:punct:]])/_/g'"
  
  set comment to "% "
  repeat with ii from 1 to cmtlength
   set comment to comment & cmtchar
  end repeat
  
  set new_title to ¬
   comment & return & ¬
   "\\" & level & "{" & title & "}" & return & ¬
   "\\label{" & labelprefix & label & "}" & return & ¬
   comment & return
 end if
 
 tell application "TeXShop" to set the selection of the front document to new_title
end tell
This macro creates a section just like the initial macro version, but this time spaces are automatically replaced in the label. That is, simply select your section title, activate the macro, and you get a nicley formatted title with a TeXShop compatible label. E.g. Mark
Hello World, this is great
and the macro will convert this to
% ==============================================================================
\section{Hello World, this is great}
\label{sec:Hello_World__this_is_great}
% ==============================================================================
You can easily adjust the macro in order to create subsection, subsubsections or chapters as well. I have different kind of comments for different section levels, all you have to change are the properties in the first line:

...
property level : "section"
property cmtchar : "-"
property cmtlength : 78
property labelprefix : "sec:"
...
Change level to "subsection" or whatever, and set the cmtchar to "*", "." or whatever you like. Changing a section level has already been subject of another blog post: Increase/Decrease Section Level with TeXShop Macros.