pygments-shell-console 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ """Pygments plugin: shell console sessions with highlighted prompts and git output."""
2
+
3
+ from .git import * # noqa: F401,F403
4
+ from .shell import ShellConsoleLexer # noqa: F401
5
+ from .style import ShellConsoleStyle # noqa: F401
@@ -0,0 +1,291 @@
1
+ """Lexers for the output of git commands."""
2
+
3
+ import re
4
+
5
+ from pygments.lexer import Lexer, RegexLexer, bygroups, default, line_re, \
6
+ using
7
+ from pygments.lexers.diff import DiffLexer
8
+ from pygments.token import STANDARD_TYPES, Comment, Generic, Token, Whitespace
9
+
10
+ __all__ = [
11
+ 'GitLexer',
12
+ 'GitStatusLexer',
13
+ 'GitShortStatusLexer',
14
+ 'GitLogLexer',
15
+ 'GitPrettyLogLexer',
16
+ 'GitShowLexer',
17
+ 'GitBranchLexer',
18
+ 'GitMergeLexer',
19
+ ]
20
+
21
+ STANDARD_TYPES.update({
22
+ Token.Git: 'git',
23
+ Token.Git.BranchLine: 'git-bl',
24
+ Token.Git.CommitHash: 'git-ch',
25
+ Token.Git.CommitDate: 'git-cd',
26
+ Token.Git.CommitMessage: 'git-cm',
27
+ Token.Git.CommitAuthor: 'git-ca',
28
+ Token.Git.Hint: 'git-hint',
29
+ Token.Git.Warning: 'git-warning',
30
+ Token.Git.Error: 'git-error',
31
+ Token.Git.Fatal: 'git-fatal',
32
+ Token.Git.Refs: 'git-r',
33
+ Token.Git.Untracked: 'git-untr',
34
+ Token.Git.Modified: 'git-mod',
35
+ Token.Git.Unmerged: 'git-unm',
36
+ Token.Git.Staged: 'git-stg',
37
+ Token.Git.Show: 'git-show',
38
+ Token.Git.Show.Header: 'git-show-h',
39
+ Token.Git.Refs.RemoteHead: 'git-rh',
40
+ Token.Git.Refs.Head: 'git-h',
41
+ Token.Git.Refs.Tag: 'git-t',
42
+ Token.Git.Refs.RemoteBranch: 'git-rb',
43
+ Token.Git.Refs.Branch: 'git-b',
44
+ })
45
+
46
+ # Remote names recognised in ref decorations, e.g. "origin/main".
47
+ _REMOTES = r'(?:origin|upstream)'
48
+
49
+
50
+ class GitLexer(RegexLexer):
51
+ """Generic git output: highlights error, fatal, hint and warning lines."""
52
+
53
+ name = 'Git'
54
+ aliases = ['git']
55
+ filenames = ['*.git']
56
+ version_added = '1.0'
57
+
58
+ tokens = {
59
+ 'root': [
60
+ (r'\n', Whitespace),
61
+ (r'^error:.*\n', Token.Git.Error),
62
+ (r'^fatal:.*\n', Token.Git.Fatal),
63
+ (r'^hint:.*\n', Token.Git.Hint),
64
+ (r'^warning:.*\n', Token.Git.Warning),
65
+ (r'^.*\n', Generic.Output),
66
+ (r'[^\n]+', Generic.Output),
67
+ ],
68
+ }
69
+
70
+
71
+ def _status_section(token):
72
+ """Rules for a `git status` file list, colouring each entry with `token`."""
73
+ return [
74
+ (r'^\s*\n', Whitespace, '#pop'),
75
+ (r'^\s+\(.*\)\n', Generic.Output),
76
+ (r'^(\s*)([^\n]+)(\n)', bygroups(Whitespace, token, Whitespace)),
77
+ ]
78
+
79
+
80
+ class GitStatusLexer(RegexLexer):
81
+ """Output of `git status`."""
82
+
83
+ name = 'Git Status'
84
+ aliases = ['git-status']
85
+ version_added = '1.0'
86
+
87
+ tokens = {
88
+ 'root': [
89
+ (r'^\n', Whitespace),
90
+ (r'\s*Untracked files:\n', Generic.Output, 'untracked'),
91
+ (r'\s*Changes not staged for commit:\n', Generic.Output, 'modified'),
92
+ (r'\s*Changes to be committed:\n', Generic.Output, 'staged'),
93
+ (r'\s*Unmerged paths:\n', Generic.Output, 'unmerged'),
94
+ (r'^.*\n', using(GitLexer)),
95
+ ],
96
+ 'untracked': _status_section(Token.Git.Untracked),
97
+ 'modified': _status_section(Token.Git.Modified),
98
+ 'staged': _status_section(Token.Git.Staged),
99
+ 'unmerged': _status_section(Token.Git.Unmerged),
100
+ }
101
+
102
+
103
+ class GitShortStatusLexer(RegexLexer):
104
+ """Output of `git status --short` / `--porcelain`."""
105
+
106
+ name = 'Git Short Status'
107
+ aliases = ['git-short-status']
108
+ version_added = '1.0'
109
+
110
+ tokens = {
111
+ 'root': [
112
+ (r'^\n', Whitespace),
113
+ (r'^(## )(No commits yet on )(\S+)(.*\n)', bygroups(
114
+ Generic.Output, Generic.Output,
115
+ Token.Git.Refs.Branch, Generic.Output,
116
+ )),
117
+ (r'^(## )(\S+?)(?:(\.\.\.)(\S+))?(\s.*\n|\n)', bygroups(
118
+ Generic.Output, Token.Git.Refs.Branch,
119
+ Generic.Output, Token.Git.Refs.RemoteBranch, Generic.Output,
120
+ )),
121
+ (r'^(\?\?)( )(.*)(\n)', bygroups(
122
+ Token.Git.Untracked, Whitespace, Generic.Output, Whitespace,
123
+ )),
124
+ (r'^(DD|AU|UD|UA|DU|AA|UU)( )(.*)(\n)', bygroups(
125
+ Token.Git.Unmerged, Whitespace, Generic.Output, Whitespace,
126
+ )),
127
+ (r'^([MTADRC ])([MTADRC ])( )(.*)(\n)', bygroups(
128
+ Token.Git.Staged, Token.Git.Modified,
129
+ Whitespace, Generic.Output, Whitespace,
130
+ )),
131
+ (r'^.*\n', using(GitLexer)),
132
+ ],
133
+ }
134
+
135
+
136
+ class GitLogLexer(RegexLexer):
137
+ """Output of `git log`, including `--oneline` and `-p`."""
138
+
139
+ name = 'Git Log'
140
+ aliases = ['git-log']
141
+ version_added = '1.0'
142
+
143
+ tokens = {
144
+ 'root': [
145
+ (r'^\n', Whitespace),
146
+ (r'^commit [0-9a-f]+', Token.Git.Show.Header, 'header'),
147
+ (r'^[0-9a-f]{7,40}(?= )', Token.Git.CommitHash, 'oneline'),
148
+ (r'^diff .*\n', using(DiffLexer), 'diff'),
149
+ (r'^.*\n', using(GitLexer)),
150
+ ],
151
+ 'header': [
152
+ (r' +', Whitespace),
153
+ (r'\(', Token.Git.Show.Header, 'refs'),
154
+ (r'\n', Whitespace, '#pop'),
155
+ ],
156
+ 'oneline': [
157
+ (r'( )(\()', bygroups(Whitespace, Token.Git.Show.Header), 'refs'),
158
+ (r'( )(.*)(\n)', bygroups(
159
+ Whitespace, Token.Git.CommitMessage, Whitespace,
160
+ ), '#pop'),
161
+ (r'\n', Whitespace, '#pop'),
162
+ ],
163
+ 'refs': [
164
+ (r' +', Whitespace),
165
+ (r'->|,', Token.Git.Show.Header),
166
+ (r'\)', Token.Git.Show.Header, '#pop'),
167
+ (_REMOTES + r'/HEAD\b', Token.Git.Refs.RemoteHead),
168
+ (r'HEAD\b', Token.Git.Refs.Head),
169
+ (r'tag: [^\s,()]+', Token.Git.Refs.Tag),
170
+ (_REMOTES + r'/[^\s,()]+', Token.Git.Refs.RemoteBranch),
171
+ (r'[^\s,()]+', Token.Git.Refs.Branch),
172
+ ],
173
+ # Patch output (`git show`, `git log -p`) lasts until the next commit.
174
+ 'diff': [
175
+ (r'^(?!commit [0-9a-f]+).*\n', using(DiffLexer)),
176
+ default('#pop'),
177
+ ],
178
+ }
179
+
180
+
181
+ class GitShowLexer(GitLogLexer):
182
+ """Output of `git show`. Same format as `git log -p`."""
183
+
184
+ name = 'Git Show'
185
+ aliases = ['git-show']
186
+
187
+
188
+ class GitPrettyLogLexer(Lexer):
189
+ """Output of `git log --graph` using the format described in the README."""
190
+
191
+ name = 'Git Pretty Log'
192
+ aliases = ['git-pretty-log']
193
+ version_added = '1.0'
194
+
195
+ _branch_line = r'([\|\\\/ _]*)'
196
+ _letters = r'0-9A-Za-zÀ-ÖØ-öø-ÿ'
197
+ _log_re = re.compile(''.join([
198
+ _branch_line, # 1. Branch line
199
+ r'(\*)', # 2. Commit asterisk
200
+ _branch_line, # 3. Branch line
201
+ r'( +)', # 4. Space
202
+ r'([a-f0-9]+)', # 5. Commit hash
203
+ r'( +)', # 6. Space
204
+ r'(-)', # 7. Commit separator
205
+ r'( +)', # 8. Space
206
+ rf'(\([{_letters} ]+\))', # 9. Date
207
+ r'(\s+)', # 10. Space
208
+ r'([^\n]+)', # 11. Commit message
209
+ r'( +)', # 12. Space
210
+ r'(-)', # 13. Commit separator
211
+ r'( +)', # 14. Space
212
+ rf'([{_letters} ]*[{_letters}]+)', # 15. Author
213
+ r'( *)', # 16. Space
214
+ r'(\([\w ->,:/.]+\))?', # 17. Refs
215
+ r'( *)', # 18. Space
216
+ r'(#[^\n]*)?', # 19. Comment
217
+ r'\n',
218
+ ]))
219
+ _branch_line_re = re.compile(_branch_line + r'\n?$')
220
+
221
+ _log_tokens = {
222
+ 1: Token.Git.BranchLine,
223
+ 3: Token.Git.BranchLine,
224
+ 4: Whitespace,
225
+ 5: Token.Git.CommitHash,
226
+ 6: Whitespace,
227
+ 8: Whitespace,
228
+ 9: Token.Git.CommitDate,
229
+ 10: Whitespace,
230
+ 11: Token.Git.CommitMessage,
231
+ 12: Whitespace,
232
+ 14: Whitespace,
233
+ 15: Token.Git.CommitAuthor,
234
+ 16: Whitespace,
235
+ 17: Token.Git.Refs,
236
+ 18: Whitespace,
237
+ 19: Comment,
238
+ }
239
+
240
+ def get_tokens_unprocessed(self, text):
241
+ for match in line_re.finditer(text):
242
+ line = match.group()
243
+ start = match.start()
244
+
245
+ match_log = self._log_re.match(line)
246
+ if match_log:
247
+ for i in range(1, self._log_re.groups + 1):
248
+ if match_log.group(i):
249
+ token = self._log_tokens.get(i, Generic.Output)
250
+ yield start + match_log.start(i), token, match_log.group(i)
251
+ yield start + match_log.end() - 1, Whitespace, '\n'
252
+ elif self._branch_line_re.match(line):
253
+ yield start, Token.Git.BranchLine, line
254
+ else:
255
+ yield start, Generic.Output, line
256
+
257
+
258
+ class GitBranchLexer(RegexLexer):
259
+ """Output of `git branch`: highlights the current branch."""
260
+
261
+ name = 'Git Branch'
262
+ aliases = ['git-branch']
263
+ version_added = '1.0'
264
+
265
+ tokens = {
266
+ 'root': [
267
+ (r'^\n', Whitespace),
268
+ (r'^(\* )(.*?)(\n)', bygroups(
269
+ using(GitLexer), Token.Git.Refs.Branch, Whitespace,
270
+ )),
271
+ (r'^.*\n', using(GitLexer)),
272
+ ],
273
+ }
274
+
275
+
276
+ class GitMergeLexer(RegexLexer):
277
+ """Diffstat output, as printed by `git merge`, `git pull`, `--stat`..."""
278
+
279
+ name = 'Git Merge'
280
+ aliases = ['git-merge', 'git-stat']
281
+ version_added = '1.0'
282
+
283
+ tokens = {
284
+ 'root': [
285
+ (r'^\n', Whitespace),
286
+ (r'^( \S+ +\| +\d+ )(\+*)(\-*)(\n)', bygroups(
287
+ Generic.Output, Generic.Inserted, Generic.Deleted, Whitespace,
288
+ )),
289
+ (r'^.*\n', using(GitLexer)),
290
+ ],
291
+ }
@@ -0,0 +1,213 @@
1
+ """Lexer for interactive shell sessions with highlighted prompts and output."""
2
+
3
+ import re
4
+
5
+ from pygments.lexer import Lexer, do_insertions, line_re
6
+ from pygments.lexers.diff import DiffLexer
7
+ from pygments.lexers.shell import BashLexer
8
+ from pygments.token import STANDARD_TYPES, Generic, Token
9
+
10
+ from .git import GitBranchLexer, GitLexer, GitLogLexer, GitMergeLexer, \
11
+ GitPrettyLogLexer, GitShortStatusLexer, GitShowLexer, GitStatusLexer
12
+
13
+ __all__ = ['ShellConsoleLexer']
14
+
15
+ STANDARD_TYPES.update({
16
+ Token.Generic.Prompt.Whitespace: 'gp-w',
17
+ Token.Generic.Prompt.UserHost: 'gp-uh',
18
+ Token.Generic.Prompt.Directory: 'gp-d',
19
+ Token.Generic.Prompt.GitBranch: 'gp-gb',
20
+ Token.Generic.Prompt.VirtualEnv: 'gp-ve',
21
+ })
22
+
23
+ # Git global options that take a separate value, e.g. `git -C repo status`.
24
+ _GIT_OPTIONS_WITH_VALUE = {'-C', '-c', '--git-dir', '--work-tree', '--namespace'}
25
+
26
+ # Unterminated here-document, e.g. `cat <<EOF` (but not `<<<` here-strings).
27
+ _HEREDOC_RE = re.compile(r"(?<!<)<<(?!<)-?\s*(['\"]?)([A-Za-z_]\w*)\1")
28
+
29
+ # Line ending in a pipe or a list operator, so bash keeps reading.
30
+ _TRAILING_OPERATOR_RE = re.compile(r'(?:\|\|?|&&)\s*\n\Z')
31
+
32
+
33
+ def _git_args(words):
34
+ """Return the arguments after `git` and its global options."""
35
+ args = words[1:]
36
+ while args and args[0].startswith('-'):
37
+ option = args.pop(0)
38
+ if option in _GIT_OPTIONS_WITH_VALUE and args:
39
+ args.pop(0)
40
+ return args
41
+
42
+
43
+ def _output_lexer_class(command):
44
+ """Choose the lexer for the output of `command`, or None for plain output."""
45
+ words = command.split()
46
+ if words and words[0] == 'sudo':
47
+ words = words[1:]
48
+ if not words:
49
+ return None
50
+
51
+ if words[0] in ('diff', 'colordiff'):
52
+ return DiffLexer
53
+ if words[0] == 'cat' and any(w.endswith(('.diff', '.patch')) for w in words):
54
+ return DiffLexer
55
+ if words[0] != 'git':
56
+ return None
57
+
58
+ args = _git_args(words)
59
+ if not args:
60
+ return GitLexer
61
+ subcommand, options = args[0], args[1:]
62
+
63
+ if subcommand == 'diff':
64
+ return GitMergeLexer if '--stat' in options else DiffLexer
65
+ if subcommand == 'show':
66
+ return GitShowLexer
67
+ if subcommand.startswith('lg') or (subcommand == 'log' and '--graph' in options):
68
+ return GitPrettyLogLexer
69
+ if subcommand == 'log':
70
+ return GitLogLexer
71
+ if subcommand == 'status':
72
+ short = any(o in ('--short', '--porcelain') or re.fullmatch(r'-[a-z]*s[a-z]*', o)
73
+ for o in options)
74
+ return GitShortStatusLexer if short else GitStatusLexer
75
+ if subcommand == 'stash' and options:
76
+ if options[0] in ('apply', 'pop'):
77
+ return GitStatusLexer
78
+ if options[0] == 'show':
79
+ return DiffLexer if {'-p', '--patch'} & set(options) else GitMergeLexer
80
+ if subcommand == 'branch':
81
+ return GitBranchLexer
82
+ if subcommand in ('merge', 'pull'):
83
+ return GitMergeLexer
84
+ return GitLexer
85
+
86
+
87
+ def _continues(code):
88
+ """Whether bash would print PS2 and keep reading after `code`."""
89
+ if code.endswith('\\\n') or _TRAILING_OPERATOR_RE.search(code):
90
+ return True
91
+
92
+ pending = []
93
+ for line in code.splitlines():
94
+ if pending:
95
+ if line.strip() == pending[0]:
96
+ pending.pop(0)
97
+ else:
98
+ pending.extend(m.group(2) for m in _HEREDOC_RE.finditer(line))
99
+ return bool(pending)
100
+
101
+
102
+ class ShellConsoleLexer(Lexer):
103
+ """
104
+ Shell session where prompts are split into their parts (virtualenv,
105
+ user@host, directory, git branch) and the output of known commands
106
+ (diff, git) is highlighted.
107
+ """
108
+
109
+ name = 'Shell Console'
110
+ aliases = ['shellconsole', 'shell-console']
111
+ filenames = ['*.sh-session', '*.shell-session']
112
+ mimetypes = ['application/x-shell-session', 'application/x-sh-session']
113
+ url = 'https://en.wikipedia.org/wiki/Unix_shell'
114
+ version_added = '1.0'
115
+
116
+ _ps1_re = re.compile(r'''
117
+ (?P<venv>\([^)]*\))?
118
+ (?P<ws1>\s*)
119
+ (?P<open>\[)?
120
+ (?:
121
+ (?P<user_host>[^\n$%\#]+@[^\s$%\#]+?)
122
+ (?:(?P<ws2>\s*)(?P<colon>:)(?P<ws3>\s*)|(?P<ws4>\s+))
123
+ (?P<current_dir>[^\s\]:$%\#]+)
124
+ )?
125
+ (?:(?P<ws5>\s+)(?P<git_branch>\([^)]+\)))?
126
+ (?P<close>\])?
127
+ (?P<ws6>\s*)
128
+ # `#` (root) is only accepted after user@host, so comments aren't prompts.
129
+ (?P<sigil>(?:[$%]|(?(user_host)\#|(?!)))\n?)
130
+ (?P<ws7>\s*)
131
+ (?P<command>.*\n?)
132
+ ''', re.VERBOSE)
133
+
134
+ _ps1_tokens = {
135
+ 'venv': Generic.Prompt.VirtualEnv,
136
+ 'user_host': Generic.Prompt.UserHost,
137
+ 'current_dir': Generic.Prompt.Directory,
138
+ 'git_branch': Generic.Prompt.GitBranch,
139
+ 'open': Generic.Prompt,
140
+ 'close': Generic.Prompt,
141
+ 'colon': Generic.Prompt,
142
+ 'sigil': Generic.Prompt,
143
+ }
144
+
145
+ _ps2 = '> '
146
+
147
+ def get_tokens_unprocessed(self, text):
148
+ innerlexer = BashLexer(**self.options)
149
+ output_lexers = {}
150
+
151
+ # Command being collected: start position, code and prompt insertions.
152
+ command_pos = 0
153
+ curcode = ''
154
+ insertions = []
155
+ # Output of the last command: start position, text and lexer class.
156
+ output_pos = 0
157
+ output = ''
158
+ output_lexer_cls = None
159
+
160
+ def flush_command():
161
+ for i, t, v in do_insertions(
162
+ insertions, innerlexer.get_tokens_unprocessed(curcode)):
163
+ yield command_pos + i, t, v
164
+
165
+ def flush_output():
166
+ if output_lexer_cls is None:
167
+ yield output_pos, Generic.Output, output
168
+ return
169
+ if output_lexer_cls not in output_lexers:
170
+ output_lexers[output_lexer_cls] = output_lexer_cls()
171
+ lexer = output_lexers[output_lexer_cls]
172
+ for i, t, v in lexer.get_tokens_unprocessed(output):
173
+ yield output_pos + i, t, v
174
+
175
+ for match in line_re.finditer(text):
176
+ line = match.group()
177
+
178
+ # Continuation of a multi-line command (backslash, heredoc...)
179
+ if insertions and _continues(curcode):
180
+ if line.startswith(self._ps2):
181
+ insertions.append((len(curcode),
182
+ [(0, Generic.Prompt, self._ps2)]))
183
+ line = line[len(self._ps2):]
184
+ curcode += line
185
+
186
+ elif m := self._ps1_re.match(line):
187
+ if output:
188
+ yield from flush_output()
189
+ output = ''
190
+
191
+ command_pos = match.start()
192
+ curcode = m.group('command')
193
+ prompt = [
194
+ (0, self._ps1_tokens.get(name, Generic.Prompt.Whitespace), value)
195
+ for name, value in m.groupdict().items()
196
+ if value and name != 'command'
197
+ ]
198
+ insertions = [(0, prompt)]
199
+
200
+ else:
201
+ if not output:
202
+ output_pos = match.start()
203
+ output_lexer_cls = _output_lexer_class(curcode)
204
+ output += line
205
+
206
+ if insertions and not _continues(curcode):
207
+ yield from flush_command()
208
+ insertions = []
209
+
210
+ if insertions:
211
+ yield from flush_command()
212
+ if output:
213
+ yield from flush_output()
@@ -0,0 +1,38 @@
1
+ """Pygments style for the shell console and git tokens."""
2
+
3
+ from pygments.style import Style
4
+ from pygments.styles.default import DefaultStyle
5
+ from pygments.token import Generic, Token
6
+
7
+
8
+ class ShellConsoleStyle(Style):
9
+ styles = DefaultStyle.styles.copy()
10
+ styles.update({
11
+ Generic.Prompt.UserHost: "#008000",
12
+ Generic.Prompt.Directory: "#19177C",
13
+ Generic.Prompt.GitBranch: "#767600",
14
+ Generic.Prompt.VirtualEnv: "#800000",
15
+
16
+ Token.Git.BranchLine: "#f00",
17
+ Token.Git.CommitHash: "#00f",
18
+ Token.Git.CommitDate: "#008000",
19
+ Token.Git.CommitAuthor: "#aaaaaa",
20
+ Token.Git.Hint: "#aaaa00",
21
+ Token.Git.Warning: "#aaaa00",
22
+ Token.Git.Error: "#f00",
23
+ Token.Git.Fatal: "bold #f00",
24
+ Token.Git.Show.Header: "#aaaa00",
25
+
26
+ Token.Git.Refs: "#00ffff",
27
+ Token.Git.Refs.Tag: "#ffff00",
28
+ Token.Git.Refs.Head: "#00ffff",
29
+ Token.Git.Refs.Branch: "#00ff00",
30
+ Token.Git.Refs.RemoteHead: "#ffcc00",
31
+ Token.Git.Refs.RemoteBranch: "#ffcc00",
32
+
33
+ Token.Git.Untracked: "#f00",
34
+ Token.Git.Modified: "#f00",
35
+ Token.Git.Unmerged: "#f00",
36
+ Token.Git.Staged: "#008000",
37
+ })
38
+ del styles[Token.Generic.Prompt]
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.5
2
+ Name: pygments-shell-console
3
+ Version: 0.9.0
4
+ Summary: Pygments lexers for shell console sessions, with highlighted prompts and git output
5
+ Project-URL: Homepage, https://github.com/joapuiib/pygments-shell-console
6
+ Requires-Python: >=3.8
7
+ Requires-Dist: pygments
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest; extra == 'test'
10
+ Description-Content-Type: text/markdown
11
+
12
+ # Pygments Shell Console
13
+ [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://www.python.org/)
14
+ [![PyPI](https://img.shields.io/badge/pypi-3775A9?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/pygments-shell-console/)
15
+
16
+ Pygments lexers for shell console sessions: highlighted prompts and git output.
17
+
18
+ ![Example](https://raw.githubusercontent.com/joapuiib/pygments-shell-console/main/docs/example.png)
19
+
20
+ ## Install
21
+
22
+ ```
23
+ pip install pygments-shell-console
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Use the `shellconsole` lexer with the `shellconsole-style` style:
29
+
30
+ ```
31
+ pygmentize -l shellconsole -O style=shellconsole-style session.txt
32
+ ```
33
+
34
+ In Markdown (MkDocs, Sphinx...), use `shellconsole` as the code block language:
35
+
36
+ ````
37
+ ```shellconsole
38
+ user@host:~/project (main) $ git status
39
+ ```
40
+ ````
41
+
42
+ ## Prompt
43
+
44
+ The prompt elements are highlighted separately:
45
+
46
+ ```
47
+ (venv) user@host:~/project (main) $
48
+ ```
49
+
50
+ Virtualenv, `user@host`, directory and git branch. Also supported:
51
+
52
+ - Root prompts (`root@host:~#`) and `[user@host ~]` style prompts.
53
+ - Continuation lines: backslash, trailing `|`, `&&` or `||`, and here-documents.
54
+
55
+ ## Output
56
+
57
+ The output lexer is chosen from the command. `sudo` and git global options
58
+ (`-C dir`, `--no-pager`...) are skipped.
59
+
60
+ | Command | Lexer | Alias |
61
+ |---|---|---|
62
+ | `diff`, `git diff`, `git stash show -p` | `DiffLexer` | |
63
+ | `git status` | `GitStatusLexer` | `git-status` |
64
+ | `git status -s`, `--porcelain` | `GitShortStatusLexer` | `git-short-status` |
65
+ | `git show` | `GitShowLexer` | `git-show` |
66
+ | `git log`, `--oneline`, `-p` | `GitLogLexer` | `git-log` |
67
+ | `git lg*`, `git log --graph` | `GitPrettyLogLexer` | `git-pretty-log` |
68
+ | `git branch` | `GitBranchLexer` | `git-branch` |
69
+ | `git merge`, `pull`, `stash show`, `diff --stat` | `GitMergeLexer` | `git-merge` |
70
+ | any other `git` command | `GitLexer` | `git` |
71
+
72
+ The git lexers can also be used on their own through their alias.
73
+
74
+ `git lg*` expects this format:
75
+
76
+ ```
77
+ log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)'
78
+ ```
79
+
80
+ More examples: [mkdocs-material-joapuiib](https://joapuiib.github.io/mkdocs-material-joapuiib/features/blocs_de_codi/#bloc-de-consola).
81
+
82
+ ## Development
83
+
84
+ ```
85
+ pip install -e '.[test]'
86
+ pytest
87
+ ```
@@ -0,0 +1,8 @@
1
+ pygments_shell_console/__init__.py,sha256=8Z_8TLhJoEHyDGEF47CLP64A-Cmwq0LSZ9-CwbD_goE,228
2
+ pygments_shell_console/git.py,sha256=6OQqV4L_Y4IzvW1l0StXVu3zjiM79ZxW0jd_MZpqIxc,9619
3
+ pygments_shell_console/shell.py,sha256=Cs8kKo6ssTXKSEz0QI-tOnv9N1amAk0Uqzq4JHqW-uU,7341
4
+ pygments_shell_console/style.py,sha256=ZIU2dzyHEUTCUiN-ayHb4qsFe5QSRfH0NVev4NWRJ-w,1247
5
+ pygments_shell_console-0.9.0.dist-info/METADATA,sha256=TjvkDHtQ4k5R_xKsQtP7njSN_jIrew-1YCLpIM5_Je8,2766
6
+ pygments_shell_console-0.9.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
7
+ pygments_shell_console-0.9.0.dist-info/entry_points.txt,sha256=buFWIELViJ-euWM8pJwmN9B_aXom1f4DcFz6v_E_918,600
8
+ pygments_shell_console-0.9.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,13 @@
1
+ [pygments.lexers]
2
+ git = pygments_shell_console.git:GitLexer
3
+ git-branch = pygments_shell_console.git:GitBranchLexer
4
+ git-log = pygments_shell_console.git:GitLogLexer
5
+ git-merge = pygments_shell_console.git:GitMergeLexer
6
+ git-pretty-log = pygments_shell_console.git:GitPrettyLogLexer
7
+ git-short-status = pygments_shell_console.git:GitShortStatusLexer
8
+ git-show = pygments_shell_console.git:GitShowLexer
9
+ git-status = pygments_shell_console.git:GitStatusLexer
10
+ shellconsole = pygments_shell_console.shell:ShellConsoleLexer
11
+
12
+ [pygments.styles]
13
+ shellconsole-style = pygments_shell_console.style:ShellConsoleStyle