Skip to content

Commit

Permalink
Merge pull request #5 from nicolasmesa/development
Browse files Browse the repository at this point in the history
Adding ctxrc to save common arguments
  • Loading branch information
nicolasmesa committed Jan 19, 2019
2 parents 84e5b11 + d413c75 commit 79ded4d
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 25 deletions.
52 changes: 36 additions & 16 deletions README.md
Expand Up @@ -18,16 +18,16 @@ $ pip install context-cli

```
$ ctx -h
usage: ctx [-h] [-d DELIMITER_TEXT] [-D DELIMITER_REGEX]
[-s DELIMITER_START_TEXT] [-S DELIMITER_START_REGEX]
[-e DELIMITER_END_TEXT] [-E DELIMITER_END_REGEX] [-x] [-X]
[-i] [-c CONTAINS_TEXT] [-C CONTAINS_REGEX]
[-m MATCHES_TEXT] [-M MATCHES_REGEX]
[-c! NOT_CONTAINS_TEXT] [-C! NOT_CONTAINS_REGEX]
[-m! NOT_MATCHES_TEXT] [-M! NOT_MATCHES_REGEX]
[-l LINE_CONTAINS_TEXT] [-L LINE_CONTAINS_REGEX]
[-l! NOT_LINE_CONTAINS_TEXT] [-L! NOT_LINE_CONTAINS_REGEX]
[-o OUTPUT_DELIMITER]
usage: ctx [-h] [-t TYPE] [-w] [-d DELIMITER_MATCHER]
[-D DELIMITER_MATCHER] [-s START_DELIMITER_MATCHER]
[-S START_DELIMITER_MATCHER] [-e END_DELIMITER_MATCHER]
[-E END_DELIMITER_MATCHER] [-x] [-X] [-i]
[-c CONTAINS_TEXT] [-C CONTAINS_REGEX] [-m MATCHES_TEXT]
[-M MATCHES_REGEX] [-c! NOT_CONTAINS_TEXT]
[-C! NOT_CONTAINS_REGEX] [-m! NOT_MATCHES_TEXT]
[-M! NOT_MATCHES_REGEX] [-l LINE_CONTAINS_TEXT]
[-L LINE_CONTAINS_REGEX] [-l! NOT_LINE_CONTAINS_TEXT]
[-L! NOT_LINE_CONTAINS_REGEX] [-o OUTPUT_DELIMITER]
[files [files ...]]
A cli tool to search with contexts.
Expand All @@ -37,17 +37,19 @@ positional arguments:
optional arguments:
-h, --help show this help message and exit
-d DELIMITER_TEXT, --delimiter-text DELIMITER_TEXT
-t TYPE, --type TYPE type of search as specified in .ctxrc
-w, --write write the current context search to .ctxrc
-d DELIMITER_MATCHER, --delimiter-text DELIMITER_MATCHER
delimiter text
-D DELIMITER_REGEX, --delimiter-regex DELIMITER_REGEX
-D DELIMITER_MATCHER, --delimiter-regex DELIMITER_MATCHER
delimiter regex
-s DELIMITER_START_TEXT, --delimiter-start-text DELIMITER_START_TEXT
-s START_DELIMITER_MATCHER, --delimiter-start-text START_DELIMITER_MATCHER
delimiter start text
-S DELIMITER_START_REGEX, --delimiter-start-regex DELIMITER_START_REGEX
-S START_DELIMITER_MATCHER, --delimiter-start-regex START_DELIMITER_MATCHER
delimiter start regex
-e DELIMITER_END_TEXT, --delimiter-end-text DELIMITER_END_TEXT
-e END_DELIMITER_MATCHER, --delimiter-end-text END_DELIMITER_MATCHER
delimiter end text
-E DELIMITER_END_REGEX, --delimiter-end-regex DELIMITER_END_REGEX
-E END_DELIMITER_MATCHER, --delimiter-end-regex END_DELIMITER_MATCHER
delimiter end regex
-x, --exclude-start-delimiter
exclude start delimiter from the context
Expand Down Expand Up @@ -116,4 +118,22 @@ $ ctx -xXi -S '^```$' -E '^```$' -c install -o "========" README.md
```


### Save common arguments

If you use `ctx` with the same arguments over and over again, you can save those arguments under a name. In
the following example, we add `my_saved_search` by using the `--type` (or `-t`) and `--write` (or `-w`).

```
$ ctx --type my_saved_search -write -xXi -s '-----' -e 'end' -o '=========='
```

And then use it (note there is no `--write` anymore)

```
$ ctx -t my_saved_search my_file.txt
```

The configuration is saved to your home folder in a `.ctxrc` file.


TODO: Add more examples
2 changes: 1 addition & 1 deletion context_cli/__init__.py
Expand Up @@ -2,6 +2,6 @@
A cli tool to search with contexts.
"""
__version__ = '0.0.dev3'
__version__ = '0.0.dev4'
__author__ = 'Nicolas Mesa'
__licence__ = 'MIT'
2 changes: 1 addition & 1 deletion context_cli/__main__.py
Expand Up @@ -4,7 +4,7 @@
def main():
import sys
try:
from .core import main
from context_cli.core import main
sys.exit(main(sys.argv))
except BrokenPipeError:
# Prevent any errors from showing up if we get a SIGPIPE (for example ctx ... | head)
Expand Down
62 changes: 58 additions & 4 deletions context_cli/core.py
Expand Up @@ -2,6 +2,12 @@
import logging
import sys

from pathlib import Path


logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)

from .context import StartAndEndDelimiterContextFactory, SingleDelimiterContextFactory
from .filter import (
# ContextFilters
Expand All @@ -13,9 +19,7 @@
ContainsTextLineFilter, ContainsRegexLineFilter, NotContainsTextLineFilter, NotContainsRegexLineFilter,
)
from .matcher import ContainsTextMatcher, RegexMatcher

logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)
from .util import CtxRc, TypeArgDoesNotExistException


def start_and_end_delimiter_context_factory_creator(start_delimiter_matcher, end_delimiter_matcher, exclude_start, exclude_end, ignore_end_delimiter):
Expand Down Expand Up @@ -135,12 +139,19 @@ def build_pipeline(context_factory, args):
return curr




def construct_arg_parser():
from . import __doc__

ap = argparse.ArgumentParser(
description=__doc__
)

ap.add_argument('-t', '--type', help='type of search as specified in .ctxrc', type=str)
ap.add_argument('-w', '--write',
help='write the current context search to .ctxrc', action='store_const', const=True, default=False)

ap.add_argument('-d', '--delimiter-text', help="delimiter text", dest='delimiter_matcher', type=ContainsTextMatcher)
ap.add_argument('-D', '--delimiter-regex', help="delimiter regex", dest='delimiter_matcher', type=RegexMatcher)

Expand Down Expand Up @@ -203,13 +214,56 @@ def construct_arg_parser():
return ap


def parse_args(ap, argv):
"""
Parses the arguments. It checks whether the `--type` arg is set, and, if it is, either writes the arguments to the
.ctxrc file or gets the args from there. If `--write` is specified, th ctxrx is written to and then this function
exits the program.
"""

args = ap.parse_args(argv[1:])

if not args.type:
return args

path = Path.home() / '.ctxrc'
ctxrc = CtxRc.from_path(path)

if args.write:
# Adding files to types doesn't make sense. Since it's a bit hard to remove the files from the argumnents, we
# add this restriction.
if args.files[0].name != '<stdin>':
ap.error("Don't specify files when writing a type")
return # We never get here but unit tests keep going since ap.error is mocked

ctxrc.add_type(args.type, argv[1:])
ctxrc.save(path)
ap.exit(0)
return # We never get here but unit tests keep going since ap.exit is mocked

try:
type_argv = ctxrc.get_type_argv(args.type)
except TypeArgDoesNotExistException as e:
ap.error(str(e))
return None # We never get here

new_argv = type_argv + argv[1:]
new_args = ap.parse_args(new_argv)
new_args.files = args.files
new_args.write = False
new_args.type = None
return new_args


def main(argv):
"""
Main method.
"""

ap = construct_arg_parser()
args = ap.parse_args(argv[1:])

args = parse_args(ap, argv)

context_factory_factory = get_context_factory_from_args(ap, args)

first = True
Expand Down
85 changes: 84 additions & 1 deletion context_cli/util.py
@@ -1,9 +1,92 @@
import re
import logging
import json
import re

logger = logging.getLogger(__name__)


class CtxRc:
"""
Contains the dict of .ctxrc
{
"version": 1,
"types": {
"md_code": {
"argv": [
"-t",
"md_code",
"-w",
"-S",
"hello",
"-E",
"world"
]
}
}
}
"""

def __init__(self, ctxrc_dict):
if ctxrc_dict == None:
ctxrc_dict = self.get_default_dict()
self.ctxrc_dict = ctxrc_dict

@property
def available_types(self):
if not 'types' in self.ctxrc_dict:
return set()
return set(self.ctxrc_dict['types'].keys())

def add_type(self, type, argv):
if not 'types' in self.ctxrc_dict:
self.ctxrc_dict['types'] = {}
self.ctxrc_dict['types'][type] = {
'argv': argv
}

def get_type_argv(self, type):
try:
return self.ctxrc_dict['types'][type]['argv']
except KeyError:
raise TypeArgDoesNotExistException(missing_type=type)

def save(self, path):
with open(path, 'w') as f:
f.write(json.dumps(self.ctxrc_dict))

@classmethod
def from_path(cls, path):
try:
with open(path, 'r') as f:
return cls(json.loads(f.read()))
except FileNotFoundError:
return cls(cls.get_default_dict())

@staticmethod
def get_default_dict():
return {
'version': 1,
'types': {}
}


class FriendlyException(Exception):
"""
Contains a friendly error message
"""
pass


class TypeArgDoesNotExistException(FriendlyException):
"""
Exception raised when the type argument passed is not in the .ctxrc
"""

def __init__(self, missing_type):
super().__init__(f"type {missing_type} doesn't exist in the .ctxrc")


def build_regexp_if_needed(maybe_regexp):
"""
Creates a regexp if the `maybe_regexp` is a str.
Expand Down

0 comments on commit 79ded4d

Please sign in to comment.