-- | Functions to access to the SC3 RTF & HTML based help systems.
module Sound.SC3.Common.Help where
import Control.Monad {- base -}
import Data.Char {- base -}
import Data.Maybe {- base -}
import System.Environment {- base -}
import System.FilePath {- filepath -}
import System.Process {- process -}
import qualified Data.List.Split as Split {- split -}
import qualified Sound.SC3.Common.Base as Base {- hsc3 -}
-- * RTF
-- | Directory containing SC3 RTF help files.
sc3_rtf_help_dir :: IO FilePath
sc3_rtf_help_dir = getEnv "SC3_RTF_HELP_DIR"
{- | Find (case-insensitively) indicated file at 'sc3_rtf_help_dir'.
Runs the command "find -name" (so UNIX only).
> sc3_rtf_find_file "SinOsc.help.rtf"
> sc3_rtf_find_file "lfsaw.help.rtf"
> sc3_rtf_find_file "softClip.rtf"
-}
sc3_rtf_find_file :: FilePath -> IO (Maybe FilePath)
sc3_rtf_find_file fn = do
d <- sc3_rtf_help_dir
r <- System.Process.readProcess "find" [d,"-iname",fn] ""
case lines r of
[] -> return Nothing
[r0] -> return (Just r0)
_ -> error "sc3_rtf_find_file: multiple files?"
-- | 'error' variant.
sc3_rtf_find_file_err :: FilePath -> IO FilePath
sc3_rtf_find_file_err = fmap (fromMaybe (error "sc3_rtf_find_file")) . sc3_rtf_find_file
-- | Run the command unrtf (so UNIX only) to convert an RTF file to a TEXT (.scd) file.
sc3_rtf_to_scd :: FilePath -> FilePath -> IO ()
sc3_rtf_to_scd rtf_fn scd_fn = do
txt <- System.Process.readProcess "unrtf" ["--text",rtf_fn] ""
let delete_trailing_whitespace = reverse . dropWhile isSpace . reverse
tidy = unlines . map delete_trailing_whitespace . drop 4 . lines
writeFile scd_fn (tidy txt)
-- | 'sc3_rtf_to_scd' of 'sc3_rtf_find_file_err', writing output to TMPDIR
sc3_rtf_help_translate :: String -> IO FilePath
sc3_rtf_help_translate nm = do
tmp <- Base.get_env_default "TMPDIR" "/tmp"
rtf_fn <- sc3_rtf_find_file_err (nm <.> "*rtf")
let scd_fn = tmp > takeFileName rtf_fn -<.> "scd"
sc3_rtf_to_scd rtf_fn scd_fn
return scd_fn
-- | 'sc3_rtf_help_translate' and run editor.
sc3_rtf_help_scd_open :: (String,[String]) -> String -> IO ()
sc3_rtf_help_scd_open (cmd,arg) nm = do
scd_fn <- sc3_rtf_help_translate nm
System.Process.callProcess cmd (arg ++ [scd_fn])
-- | 'sc3_rtf_help_scd_open' with emacsclient --no-wait.
--
-- > sc3_rtf_help_scd_open_emacs "lfsaw"
sc3_rtf_help_scd_open_emacs :: String -> IO ()
sc3_rtf_help_scd_open_emacs = sc3_rtf_help_scd_open ("emacsclient",["--no-wait"])
-- * SC-DOC
-- | URL for online SC-DOC SuperCollider documentation.
sc3_scdoc_help_url :: String
sc3_scdoc_help_url = "http://doc.sccode.org/"
-- | Read the environment variable @SC3_SCDOC_HTML_HELP_DIR@.
-- The default value is @~\/.local\/share\/SuperCollider/Help@.
sc3_scdoc_help_dir :: IO String
sc3_scdoc_help_dir = do
h <- getEnv "HOME"
let d = h > ".local/share/SuperCollider/Help"
Base.get_env_default "SC3_SCDOC_HTML_HELP_DIR" d
-- | Path to indicated SC3 class help file.
--
-- > sc3_scdoc_help_class "SinOsc" == "Classes/SinOsc.html"
sc3_scdoc_help_class :: String -> String
sc3_scdoc_help_class c = "Classes" > c <.> "html"
-- | Generate path to indicated SC3 operator help file.
--
-- > sc3_scdoc_help_operator "+" == "Overviews/Operators.html#+"
sc3_scdoc_help_operator :: String -> FilePath
sc3_scdoc_help_operator = (++) "Overviews/Operators.html#"
-- | Generate path to indicated SC3 method help.
--
-- > sc3_scdoc_help_method '*' ("C","m") == "Classes/C.html#*m"
sc3_scdoc_help_method :: Char -> (String,String) -> FilePath
sc3_scdoc_help_method z (c,m) = "Classes" > c <.> "html#" ++ [z] ++ m
-- | Generate path to indicated SC3 class method help.
--
-- > sc3_scdoc_help_class_method ("C","m") == "Classes/C.html#*m"
sc3_scdoc_help_class_method :: (String,String) -> FilePath
sc3_scdoc_help_class_method = sc3_scdoc_help_method '*'
-- | Generate path to indicated SC3 instance method help.
--
-- > sc3_scdoc_help_instance_method ("C","m") == "Classes/C.html#-m"
sc3_scdoc_help_instance_method :: (String,String) -> FilePath
sc3_scdoc_help_instance_method = sc3_scdoc_help_method '-'
{- | SC3 help path documenting x.
> sc3_scdoc_help_path "Operator.distort" == "Overviews/Operators.html#.distort"
> sc3_scdoc_help_path "Collection.*fill" == "Classes/Collection.html#*fill"
> sc3_scdoc_help_path "Collection.inject" == "Classes/Collection.html#-inject"
> sc3_scdoc_help_path "SinOsc" == "Classes/SinOsc.html"
-}
sc3_scdoc_help_path :: String -> String
sc3_scdoc_help_path s = do
case Split.splitOn "." s of
["Operator",m] -> sc3_scdoc_help_operator m
[c,'*':m] -> sc3_scdoc_help_class_method (c,m)
[c,m] -> sc3_scdoc_help_instance_method (c,m)
_ -> sc3_scdoc_help_class s
{- | Open SC3 help path, either the local file or the online version.
Use @BROWSER@ or @x-www-browser@.
> Base.get_env_default "BROWSER" "x-www-browser"
> sc3_scdoc_help_open True (sc3_scdoc_help_path "SinOsc")
> sc3_scdoc_help_open True (sc3_scdoc_help_path "Collection.*fill")
> sc3_scdoc_help_open False (sc3_scdoc_help_path "Collection.inject")
-}
sc3_scdoc_help_open :: Bool -> String -> IO ()
sc3_scdoc_help_open use_loc p = do
d <- sc3_scdoc_help_dir
b <- Base.get_env_default "BROWSER" "x-www-browser"
let u = if use_loc then "file://" ++ (d > p) else sc3_scdoc_help_url ++ p
void (System.Process.rawSystem b [u])
{- | Generate path to indicated SC3 instance method help.
Adds initial forward slash if not present.
> let r = "Reference/Server-Command-Reference.html#/b_alloc"
> sc3_scdoc_help_server_command_path "b_alloc" == r
-}
sc3_scdoc_help_server_command_path :: String -> FilePath
sc3_scdoc_help_server_command_path c =
let c' = case c of
'/':_ -> c
_ -> '/':c
in "Reference/Server-Command-Reference.html" ++ ('#' : c')
{- | 'sc3_scdoc_help_open' of 'sc3_server_command_path'
> sc3_scdoc_help_server_command_open True "s_new"
> sc3_scdoc_help_server_command_open False "/b_allocRead"
-}
sc3_scdoc_help_server_command_open :: Bool -> String -> IO ()
sc3_scdoc_help_server_command_open use_loc =
sc3_scdoc_help_open use_loc .
sc3_scdoc_help_server_command_path