mirror of
https://github.com/simonmichael/hledger.git
synced 2024-11-07 21:15:19 +03:00
lib,cli,ui: In ReportOpts, store query terms term-by-term in a list in
querystring_. This helps deal with tricky quoting issues, as we no longer have to make sure everything is quoted properly before merging it into a string.
This commit is contained in:
parent
c010a6df48
commit
83a518af99
@ -20,6 +20,7 @@ module Hledger.Query (
|
||||
generatedTransactionTag,
|
||||
-- * parsing
|
||||
parseQuery,
|
||||
parseQueryList,
|
||||
simplifyQuery,
|
||||
filterQuery,
|
||||
-- * accessors
|
||||
@ -141,9 +142,23 @@ data QueryOpt = QueryOptInAcctOnly AccountName -- ^ show an account register fo
|
||||
-- showAccountMatcher _ = Nothing
|
||||
|
||||
|
||||
-- | Convert a query expression containing zero or more
|
||||
-- space-separated terms to a query and zero or more query options; or
|
||||
-- return an error message if query parsing fails.
|
||||
-- | A version of parseQueryList which acts on a single Text of
|
||||
-- space-separated terms.
|
||||
--
|
||||
-- The usual shell quoting rules are assumed. When a pattern contains
|
||||
-- whitespace, it (or the whole term including prefix) should be enclosed
|
||||
-- in single or double quotes.
|
||||
--
|
||||
-- >>> parseQuery nulldate "expenses:dining out"
|
||||
-- Right (Or [Acct (RegexpCI "expenses:dining"),Acct (RegexpCI "out")],[])
|
||||
--
|
||||
-- >>> parseQuery nulldate "\"expenses:dining out\""
|
||||
-- Right (Acct (RegexpCI "expenses:dining out"),[])
|
||||
parseQuery :: Day -> T.Text -> Either String (Query,[QueryOpt])
|
||||
parseQuery d = parseQueryList d . words'' prefixes
|
||||
|
||||
-- | Convert a list of query expression containing to a query and zero
|
||||
-- or more query options; or return an error message if query parsing fails.
|
||||
--
|
||||
-- A query term is either:
|
||||
--
|
||||
@ -161,10 +176,6 @@ data QueryOpt = QueryOptInAcctOnly AccountName -- ^ show an account register fo
|
||||
--
|
||||
-- inacct:FULLACCTNAME
|
||||
--
|
||||
-- The usual shell quoting rules are assumed. When a pattern contains
|
||||
-- whitespace, it (or the whole term including prefix) should be enclosed
|
||||
-- in single or double quotes.
|
||||
--
|
||||
-- Period expressions may contain relative dates, so a reference date is
|
||||
-- required to fully parse these.
|
||||
--
|
||||
@ -173,15 +184,8 @@ data QueryOpt = QueryOptInAcctOnly AccountName -- ^ show an account register fo
|
||||
-- 2. multiple description patterns are OR'd together
|
||||
-- 3. multiple status patterns are OR'd together
|
||||
-- 4. then all terms are AND'd together
|
||||
--
|
||||
-- >>> parseQuery nulldate "expenses:dining out"
|
||||
-- Right (Or [Acct (RegexpCI "expenses:dining"),Acct (RegexpCI "out")],[])
|
||||
--
|
||||
-- >>> parseQuery nulldate "\"expenses:dining out\""
|
||||
-- Right (Acct (RegexpCI "expenses:dining out"),[])
|
||||
parseQuery :: Day -> T.Text -> Either String (Query,[QueryOpt])
|
||||
parseQuery d s = do
|
||||
let termstrs = words'' prefixes s
|
||||
parseQueryList :: Day -> [T.Text] -> Either String (Query, [QueryOpt])
|
||||
parseQueryList d termstrs = do
|
||||
eterms <- sequence $ map (parseQueryTerm d) termstrs
|
||||
let (pats, opts) = partitionEithers eterms
|
||||
(descpats, pats') = partition queryIsDesc pats
|
||||
|
@ -92,7 +92,7 @@ data ReportOpts = ReportOpts {
|
||||
,no_elide_ :: Bool
|
||||
,real_ :: Bool
|
||||
,format_ :: StringFormat
|
||||
,querystring_ :: T.Text
|
||||
,querystring_ :: [T.Text]
|
||||
--
|
||||
,average_ :: Bool
|
||||
-- for posting reports (register)
|
||||
@ -141,7 +141,7 @@ defreportopts = ReportOpts
|
||||
, no_elide_ = False
|
||||
, real_ = False
|
||||
, format_ = def
|
||||
, querystring_ = ""
|
||||
, querystring_ = []
|
||||
, average_ = False
|
||||
, related_ = False
|
||||
, txn_dates_ = False
|
||||
@ -168,8 +168,7 @@ rawOptsToReportOpts rawopts = do
|
||||
|
||||
let colorflag = stringopt "color" rawopts
|
||||
formatstring = maybestringopt "format" rawopts
|
||||
querystring = T.pack . unwords . map quoteIfNeeded $
|
||||
listofstringopt "args" rawopts -- doesn't handle an arg like "" right
|
||||
querystring = map T.pack $ listofstringopt "args" rawopts -- doesn't handle an arg like "" right
|
||||
|
||||
format <- case parseStringFormat <$> formatstring of
|
||||
Nothing -> return defaultBalanceLineFormat
|
||||
@ -237,7 +236,7 @@ defreportspec = ReportSpec
|
||||
-- | Generate a ReportSpec from a set of ReportOpts on a given day.
|
||||
reportOptsToSpec :: Day -> ReportOpts -> Either String ReportSpec
|
||||
reportOptsToSpec day ropts = do
|
||||
(argsquery, queryopts) <- parseQuery day $ querystring_ ropts
|
||||
(argsquery, queryopts) <- parseQueryList day $ querystring_ ropts
|
||||
return ReportSpec
|
||||
{ rsOpts = ropts
|
||||
, rsToday = day
|
||||
|
@ -173,7 +173,7 @@ asDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}
|
||||
<+> toggles
|
||||
<+> str (" account " ++ if ishistorical then "balances" else "changes")
|
||||
<+> borderPeriodStr (if ishistorical then "at end of" else "in") (period_ ropts)
|
||||
<+> borderQueryStr (T.unpack $ querystring_ ropts)
|
||||
<+> borderQueryStr (unwords . map (quoteIfNeeded . T.unpack) $ querystring_ ropts)
|
||||
<+> borderDepthStr mdepth
|
||||
<+> str (" ("++curidx++"/"++totidx++")")
|
||||
<+> (if ignore_assertions_ $ inputopts_ copts
|
||||
|
@ -200,7 +200,7 @@ rsDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}
|
||||
<+> togglefilters
|
||||
<+> str " transactions"
|
||||
-- <+> str (if ishistorical then " historical total" else " period total")
|
||||
<+> borderQueryStr (T.unpack $ querystring_ ropts)
|
||||
<+> borderQueryStr (unwords . map (quoteIfNeeded . T.unpack) $ querystring_ ropts)
|
||||
-- <+> str " and subs"
|
||||
<+> borderPeriodStr "in" (period_ ropts)
|
||||
<+> str " ("
|
||||
|
@ -97,7 +97,7 @@ tsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{
|
||||
<+> withAttr ("border" <> "bold") (str $ show i)
|
||||
<+> str (" of "++show (length nts))
|
||||
<+> togglefilters
|
||||
<+> borderQueryStr (T.unpack $ querystring_ ropts)
|
||||
<+> borderQueryStr (unwords . map (quoteIfNeeded . T.unpack) $ querystring_ ropts)
|
||||
<+> str (" in "++T.unpack (replaceHiddenAccountsNameWith "All" acct)++")")
|
||||
<+> (if ignore_assertions_ $ inputopts_ copts then withAttr ("border" <> "query") (str " ignoring balance assertions") else str "")
|
||||
where
|
||||
|
@ -243,7 +243,8 @@ setFilter :: String -> UIState -> UIState
|
||||
setFilter s ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =
|
||||
ui{aopts=uopts{cliopts_=copts{reportspec_=newrspec}}}
|
||||
where
|
||||
newrspec = either (const rspec) id $ reportOptsToSpec (rsToday rspec) ropts{querystring_=T.pack s}
|
||||
newrspec = either (const rspec) id $ reportOptsToSpec (rsToday rspec) ropts{querystring_=querystring}
|
||||
querystring = words'' prefixes $ T.pack s
|
||||
|
||||
-- | Reset some filters & toggles.
|
||||
resetFilter :: UIState -> UIState
|
||||
@ -255,7 +256,7 @@ resetFilter ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rsp
|
||||
empty_=True
|
||||
,statuses_=[]
|
||||
,real_=False
|
||||
,querystring_=""
|
||||
,querystring_=[]
|
||||
--,period_=PeriodAll
|
||||
}}}}}
|
||||
|
||||
@ -312,7 +313,8 @@ showMinibuffer :: UIState -> UIState
|
||||
showMinibuffer ui = setMode (Minibuffer e) ui
|
||||
where
|
||||
e = applyEdit gotoEOL $ editor MinibufferEditor (Just 1) oldq
|
||||
oldq = T.unpack . querystring_ . rsOpts . reportspec_ . cliopts_ $ aopts ui
|
||||
oldq = unwords . map (quoteIfNeeded . T.unpack)
|
||||
. querystring_ . rsOpts . reportspec_ . cliopts_ $ aopts ui
|
||||
|
||||
-- | Close the minibuffer, discarding any edit in progress.
|
||||
closeMinibuffer :: UIState -> UIState
|
||||
|
@ -76,8 +76,8 @@ aregister opts@CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do
|
||||
-- the first argument specifies the account, any remaining arguments are a filter query
|
||||
(apat,querystring) <- case listofstringopt "args" rawopts of
|
||||
[] -> fail "aregister needs an account, please provide an account name or pattern"
|
||||
(a:as) -> return (a, T.pack . unwords $ map quoteIfNeeded as)
|
||||
argsquery <- either fail (return . fst) $ parseQuery d querystring
|
||||
(a:as) -> return (a, map T.pack as)
|
||||
argsquery <- either fail (return . fst) $ parseQueryList d querystring
|
||||
let
|
||||
acct = headDef (error' $ show apat++" did not match any account") -- PARTIAL:
|
||||
. filterAccts $ journalAccountNames j
|
||||
|
@ -31,12 +31,12 @@ tags CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do
|
||||
let args = listofstringopt "args" rawopts
|
||||
mtagpat <- mapM (either Fail.fail pure . toRegexCI) $ headMay args
|
||||
let
|
||||
querystring = T.pack . unwords . map quoteIfNeeded $ drop 1 args
|
||||
querystring = map T.pack $ drop 1 args
|
||||
values = boolopt "values" rawopts
|
||||
parsed = boolopt "parsed" rawopts
|
||||
empty = empty_ $ rsOpts rspec
|
||||
|
||||
argsquery <- either usageError (return . fst) $ parseQuery d querystring
|
||||
argsquery <- either usageError (return . fst) $ parseQueryList d querystring
|
||||
let
|
||||
q = simplifyQuery $ And [queryFromFlags $ rsOpts rspec, argsquery]
|
||||
txns = filter (q `matchesTransaction`) $ jtxns $ journalSelectingAmountFromOpts (rsOpts rspec) j
|
||||
|
@ -1,17 +1,17 @@
|
||||
# 1. account pattern with space
|
||||
hledger -f- register 'a a'
|
||||
<<<
|
||||
<
|
||||
2010/3/1 x
|
||||
a a 1
|
||||
b
|
||||
>>>
|
||||
|
||||
$ hledger -f- register 'a a'
|
||||
>
|
||||
2010-03-01 x a a 1 1
|
||||
>>>=0
|
||||
>=0
|
||||
|
||||
#
|
||||
# 2. description pattern with space
|
||||
hledger -f- register desc:'x x'
|
||||
<<<
|
||||
<
|
||||
2010/3/1 x
|
||||
a 1
|
||||
b
|
||||
@ -19,19 +19,38 @@ hledger -f- register desc:'x x'
|
||||
2010/3/2 x x
|
||||
a 1
|
||||
b
|
||||
>>>
|
||||
|
||||
$ hledger -f- register desc:'x x'
|
||||
>
|
||||
2010-03-02 x x a 1 1
|
||||
b -1 0
|
||||
>>>=0
|
||||
>=0
|
||||
|
||||
#
|
||||
# 3. multiple patterns, spaced and punctuated patterns
|
||||
hledger -f- register 'a a' "'b"
|
||||
<<<
|
||||
<
|
||||
2011/9/11
|
||||
a a 1
|
||||
'b
|
||||
>>>
|
||||
|
||||
$ hledger -f- register 'a a' "'b"
|
||||
>
|
||||
2011-09-11 a a 1 1
|
||||
'b -1 0
|
||||
>>>=0
|
||||
>=0
|
||||
|
||||
#
|
||||
# 4. patterns with quotation marks in them
|
||||
<
|
||||
2020-09-19 Quoting
|
||||
assets:bank -5
|
||||
assets:unquoted 5
|
||||
|
||||
2020-09-20 Quoting
|
||||
assets:bank -5
|
||||
assets:"quoted" 5
|
||||
|
||||
$ hledger -f- register '"quoted'
|
||||
>
|
||||
2020-09-20 Quoting assets:"quoted" 5 5
|
||||
>=0
|
||||
|
Loading…
Reference in New Issue
Block a user