packages feed

hat-2.9.0.0: tools/SrcRefViewer.hs

-- Simple program to display a src reference.
-- Reports the filename and location, and shows a section of the file
-- with the cursor on the actual location.  We try to place the
-- important line as near the centre of the screen as possible.

module Main where

import System.Environment (getProgName, getArgs)
import System.Process (system)
import System.Exit    (exitWith, ExitCode(..))
import System.IO      (hSetBuffering,BufferMode(..),stdin,stdout)
import Data.Char      (isSpace,isDigit)
import Run            (runAndReadStdout)
import HighlightStyle (getTerminalSize,cls,goto,highlight,lineWrap
                      ,Highlight(..),Colour(..))

main = do
    args <- System.Environment.getArgs
    case args of
      [filename,sline,scol] ->
              let line = (read sline)::Int
                  column = (read scol)::Int in
              display filename line column 0 0
      [filename,sline,scol,sline2,scol2] ->
              let line = (read sline)::Int
                  column = (read scol)::Int
                  endline = (read sline2)::Int
                  endcolumn = (read scol2)::Int in
              display filename line column endline endcolumn
      _ -> do prog <- System.Environment.getProgName
              putStrLn ("Usage: "++prog++" srcfile line col [endline endcol]")
              exitWith (ExitFailure 1)

display :: FilePath -> Int -> Int -> Int -> Int -> IO ()
display srcfile line column endline endcolumn = do
    len <- runAndReadStdout ("wc -l "++srcfile)
    let n = read (takeWhile isDigit (dropWhile isSpace len))
    case n of
      0 -> do putStrLn ("File "++srcfile++" not found.")
              exitWith (ExitFailure 1)
      _ -> do (width,height) <- getTerminalSize
              f <- readFile srcfile
              hSetBuffering stdin NoBuffering
              hSetBuffering stdout NoBuffering
              let middle= (height `div` 2) - 1
                  trim  = if line < middle then 0 else line - middle
                  fs    = (expandTabs . unlines . take (height-2)
                          . drop trim . lines) f
                  line' = line-trim
                  (a,b,c) = split (line',column,endline-trim,endcolumn) fs
              putStr (cls ++ goto 1 1 ++ lineWrap False)
              putStr (highlight [Bold] ("---- "++srcfile++" ---- line: "
                                       ++show line++" ---- column: "
                                       ++show column++" ----"))
              if endline==0
                then putStr (goto 1 2 ++ fs ++ goto column (line'+1))
                else putStr (goto 1 2 ++ a
                             ++ highlight [Bold, Foreground Magenta] b ++ c
                             ++ goto column (line'+1))
              system ("stty -icanon min 1 -echo")
              awaitQuit
              system ("stty icanon echo")
              putStr (goto 1 height)
              return ()

split :: (Int,Int,Int,Int) -> String -> (String,String,String)
split (sl,sc,el,ec) str
  | sl==el    =
    let (a,b) = splitAt (el-1) (lines str)
        (c,d) = splitAt ec (head b)
        (e,f) = splitAt (sc-1) c
    in (unlines a++e, f, unlines (d:tail b))
  | otherwise =
    let ls = lines str
        (a,b)   = splitAt (el-1) (lines str)
        (a',b') = splitAt  ec    (head b)
        (p,q)   = splitAt (sl-1) a
        (p',q') = splitAt (sc-1) (head q)
    in (unlines p++p', unlines (q':tail q)++a', unlines (b':tail b))

awaitQuit :: IO ()
awaitQuit = do
  q <- getChar
  case q of
    'q' -> return ()
    'x' -> return ()
    _   -> awaitQuit

expandTabs :: String -> String
expandTabs = expand 0
  where expand n []         = []
        expand n ('\n':xs)  = '\n': expand 0 xs
        expand 8 ('\t':xs)  = replicate 8     ' ' ++ expand 0 xs
        expand n ('\t':xs)  = replicate (8-n) ' ' ++ expand 0 xs
        expand 8 (x:xs)     = x : expand 1 xs
        expand n (x:xs)     = x : expand (n+1) xs