mirror of
https://github.com/tstack/lnav.git
synced 2024-10-05 17:17:37 +03:00
[markdown] add support for going to an anchor
This commit is contained in:
parent
fd956c66d3
commit
87791ec94b
1
.github/workflows/c-cpp.yml
vendored
1
.github/workflows/c-cpp.yml
vendored
@ -45,6 +45,7 @@ jobs:
|
||||
--exclude src/ww898
|
||||
--exclude src/yajl
|
||||
--exclude test
|
||||
--exclude src/data_scanner_re.cc
|
||||
--gcov-options '\-lp'
|
||||
|
||||
build:
|
||||
|
12
NEWS
12
NEWS
@ -18,6 +18,16 @@ lnav v0.11.0:
|
||||
TEXT view. The breadcrumb bar at the top will also be updated
|
||||
depending on the section of the document that you are in and you
|
||||
can use it to jump to different parts of the doc.
|
||||
* The ":goto" command will now accept anchor links (i.e. #section-id)
|
||||
as an argument when the text file being viewed has sections. You
|
||||
can also specify an anchor when opening a file by appending
|
||||
"#<link-name>". For example, "README.md#screenshot".
|
||||
* Log message comments are now treated as markdown and rendered
|
||||
accordingly in the overlay. Multi-line comments are now supported
|
||||
as well.
|
||||
* Metadata embedded in files can now be accessed by the
|
||||
"lnav_file_metadata" table. Currently, only the front-matter in
|
||||
Markdown files is supported.
|
||||
* Added an integration with regex101.com to make it easier to edit
|
||||
log message regular expressions. Using the new "management CLI"
|
||||
(activated by the -m option), a log format can be created from
|
||||
@ -88,6 +98,8 @@ lnav v0.11.0:
|
||||
* Added an lnav_views_echo table that is a real SQLite table that
|
||||
you can create TRIGGERs on in order to perform actions when
|
||||
scrolling in a view.
|
||||
* Added a "yaml_to_json()" SQL function that converts a YAML
|
||||
document to the equivalent JSON.
|
||||
|
||||
Breaking Changes:
|
||||
* Formats definitions are now checked to ensure that values have a
|
||||
|
@ -1,9 +1,11 @@
|
||||
<!-- This is a comment for testing purposes -->
|
||||
|
||||
[![Build](https://github.com/tstack/lnav/workflows/ci-build/badge.svg)](https://github.com/tstack/lnav/actions?query=workflow%3Aci-build)
|
||||
[![Docs](https://readthedocs.org/projects/lnav/badge/?version=latest&style=plastic)](https://docs.lnav.org)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/tstack/lnav/badge.svg?branch=master)](https://coveralls.io/github/tstack/lnav?branch=master)
|
||||
[![lnav](https://snapcraft.io//lnav/badge.svg)](https://snapcraft.io/lnav)
|
||||
[![lnav](https://snapcraft.io/lnav/badge.svg)](https://snapcraft.io/lnav)
|
||||
|
||||
[<img src="https://assets-global.website-files.com/6257adef93867e50d84d30e2/62594fddd654fc29fcc07359_cb48d2a8d4991281d7a6a95d2f58195e.svg" height="20">](https://discord.gg/erBPnKwz7R)
|
||||
[<img src="https://assets-global.website-files.com/6257adef93867e50d84d30e2/62594fddd654fc29fcc07359_cb48d2a8d4991281d7a6a95d2f58195e.svg" height="20"/>](https://discord.gg/erBPnKwz7R)
|
||||
|
||||
_This is the source repository for **lnav**, visit [https://lnav.org](https://lnav.org) for a high level overview._
|
||||
|
||||
|
54
demo/loggen.py
Executable file
54
demo/loggen.py
Executable file
@ -0,0 +1,54 @@
|
||||
import datetime
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
MSGS = [
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
|
||||
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
|
||||
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
]
|
||||
|
||||
GLOG_DATE_FMT = "%Y%m%d %H:%M:%S"
|
||||
|
||||
START_TIME = datetime.datetime.fromtimestamp(1490191111)
|
||||
|
||||
try:
|
||||
shutil.rmtree("/tmp/demo")
|
||||
os.makedirs("/tmp/demo")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
PIDS = [
|
||||
"123",
|
||||
"123",
|
||||
"123",
|
||||
"121",
|
||||
"124",
|
||||
"123",
|
||||
"61456",
|
||||
"61456",
|
||||
"61457",
|
||||
]
|
||||
|
||||
LOG_LOCS = [
|
||||
"demo.cc:123",
|
||||
"demo.cc:352",
|
||||
"loader.cc:13",
|
||||
"loader.cc:552",
|
||||
"blaster.cc:352",
|
||||
"blaster.cc:112",
|
||||
"blaster.cc:6782",
|
||||
]
|
||||
|
||||
CURR_TIME = START_TIME
|
||||
for _index in range(0, int(sys.argv[1])):
|
||||
CURR_TIME += datetime.timedelta(seconds=random.randrange(1, 22))
|
||||
print("I%s.%06d %s %s] %s" % (
|
||||
CURR_TIME.strftime(GLOG_DATE_FMT),
|
||||
random.randrange(0, 100000),
|
||||
random.choice(PIDS),
|
||||
random.choice(LOG_LOCS),
|
||||
random.choice(MSGS)))
|
18
docs/tutorials/tutorial-lib/configs/tutorial1/config.json
Normal file
18
docs/tutorials/tutorial-lib/configs/tutorial1/config.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://lnav.org/schemas/config-v1.schema.json",
|
||||
"global": {
|
||||
"lnav_tutorial_name": "tutorial1"
|
||||
},
|
||||
"ui": {
|
||||
"keymap-defs": {
|
||||
"default": {
|
||||
"x79": {
|
||||
"command": "|lnav-tutorial-key-handler next"
|
||||
},
|
||||
"x59": {
|
||||
"command": "|lnav-tutorial-key-handler prev"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
|
||||
;SELECT filepath AS tutorial_path FROM lnav_file
|
||||
WHERE filepath GLOB '*/tutorial1/index.md' LIMIT 1
|
||||
;SELECT CASE
|
||||
WHEN $1 = 'next' AND
|
||||
step < (SELECT max(step) FROM lnav_tutorial_steps WHERE name = $lnav_tutorial_name)
|
||||
THEN step + 1
|
||||
WHEN $1 = 'prev' AND step > 1 THEN step - 1
|
||||
ELSE step
|
||||
END AS new_step
|
||||
FROM lnav_tutorial_step WHERE name = $lnav_tutorial_name
|
||||
;SELECT CASE
|
||||
WHEN $1 = 'next' AND
|
||||
step = (SELECT max(step) FROM lnav_tutorial_steps WHERE name = $lnav_tutorial_name)
|
||||
THEN '#conclusion'
|
||||
ELSE '#step-' || $new_step
|
||||
END AS new_anchor
|
||||
FROM lnav_tutorial_step WHERE name = $lnav_tutorial_name
|
||||
;UPDATE lnav_tutorial_step SET step = $new_step WHERE name = $lnav_tutorial_name
|
||||
;UPDATE lnav_views SET top_meta = json_object(
|
||||
'file', $tutorial_path,
|
||||
'anchor', $new_anchor
|
||||
)
|
||||
WHERE name = 'text'
|
||||
:switch-to-view text
|
||||
;UPDATE lnav_views SET top = 0
|
||||
WHERE name = 'log'
|
||||
;REPLACE INTO lnav_user_notifications (id, views, message)
|
||||
SELECT * FROM lnav_tutorial_log_notification;
|
141
docs/tutorials/tutorial-lib/formats/tutorial-lib/tutorial.sql
Normal file
141
docs/tutorials/tutorial-lib/formats/tutorial-lib/tutorial.sql
Normal file
@ -0,0 +1,141 @@
|
||||
CREATE TABLE lnav_tutorial_step
|
||||
(
|
||||
name TEXT NOT NULL PRIMARY KEY,
|
||||
step INTEGER NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO lnav_tutorial_step
|
||||
VALUES ('tutorial1', 1);
|
||||
|
||||
CREATE TABLE lnav_tutorial_steps
|
||||
(
|
||||
name TEXT NOT NULL,
|
||||
step INTEGER NOT NULL,
|
||||
achievements TEXT NOT NULL,
|
||||
PRIMARY KEY (name, step)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lnav_tutorial_progress
|
||||
(
|
||||
name TEXT NOT NULL,
|
||||
step INTEGER NOT NULL,
|
||||
achieved TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (name, step, achieved)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lnav_tutorial_lines
|
||||
(
|
||||
name TEXT NOT NULL,
|
||||
step INTEGER NOT NULL,
|
||||
view_ptr TEXT NOT NULL,
|
||||
view_value TEXT NOT NULL,
|
||||
achievement TEXT NOT NULL,
|
||||
log_comment TEXT
|
||||
);
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS add_tutorial_data
|
||||
AFTER INSERT
|
||||
ON lnav_events
|
||||
WHEN jget(new.content, '/$schema') = 'https://lnav.org/event-file-format-detected-v1.schema.json' AND
|
||||
jget(new.content, '/format') = 'text/markdown'
|
||||
BEGIN
|
||||
INSERT INTO lnav_tutorial_steps
|
||||
SELECT jget(tutorial_meta, '/name'),
|
||||
key + 1,
|
||||
value
|
||||
FROM (SELECT yaml_to_json(lnav_file_metadata.content) AS tutorial_meta
|
||||
FROM lnav_file_metadata
|
||||
WHERE filepath = jget(new.content, '/filename')) AS meta_content,
|
||||
json_each(jget(meta_content.tutorial_meta, '/steps'));
|
||||
|
||||
REPLACE INTO lnav_tutorial_lines
|
||||
SELECT name,
|
||||
step,
|
||||
jget(value, '/view_ptr'),
|
||||
jget(value, '/view_value'),
|
||||
key,
|
||||
jget(value, '/comment')
|
||||
FROM lnav_tutorial_steps,
|
||||
json_each(achievements)
|
||||
WHERE jget(value, '/view_ptr') IS NOT NULL;
|
||||
|
||||
REPLACE INTO lnav_user_notifications (id, views, message)
|
||||
SELECT *
|
||||
FROM lnav_tutorial_log_notification;
|
||||
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS lnav_tutorial_view_listener UPDATE OF top
|
||||
ON lnav_views_echo
|
||||
WHEN new.name = 'log'
|
||||
BEGIN
|
||||
INSERT OR IGNORE INTO lnav_tutorial_progress
|
||||
SELECT lnav_tutorial_lines.name,
|
||||
lnav_tutorial_lines.step,
|
||||
achievement
|
||||
FROM lnav_tutorial_step,
|
||||
lnav_tutorial_lines
|
||||
WHERE lnav_tutorial_step.step = lnav_tutorial_lines.step
|
||||
AND jget(json_object('top', new.top,
|
||||
'left', new.left,
|
||||
'search', new.search),
|
||||
view_ptr) = view_value;
|
||||
UPDATE all_logs
|
||||
SET log_comment = (SELECT log_comment
|
||||
FROM lnav_tutorial_step,
|
||||
lnav_tutorial_lines
|
||||
WHERE lnav_tutorial_step.step = lnav_tutorial_lines.step
|
||||
AND lnav_tutorial_lines.log_comment IS NOT NULL
|
||||
AND jget(json_object('top', new.top,
|
||||
'left', new.left,
|
||||
'search', new.search), view_ptr) = view_value)
|
||||
WHERE log_line = new.top
|
||||
AND log_comment IS NULL;
|
||||
END;
|
||||
|
||||
CREATE TABLE lnav_tutorial_message
|
||||
(
|
||||
msgid INTEGER PRIMARY KEY,
|
||||
msg TEXT
|
||||
);
|
||||
|
||||
CREATE VIEW lnav_tutorial_current_achievements AS
|
||||
SELECT key AS achievement, value
|
||||
FROM lnav_tutorial_step,
|
||||
lnav_tutorial_steps, json_each(lnav_tutorial_steps.achievements)
|
||||
WHERE lnav_tutorial_step.step = lnav_tutorial_steps.step;
|
||||
|
||||
CREATE VIEW lnav_tutorial_current_progress AS
|
||||
SELECT achieved
|
||||
FROM lnav_tutorial_step,
|
||||
lnav_tutorial_progress
|
||||
WHERE lnav_tutorial_step.step = lnav_tutorial_progress.step;
|
||||
|
||||
CREATE VIEW lnav_tutorial_remaining_achievements AS
|
||||
SELECT *
|
||||
FROM lnav_tutorial_current_achievements
|
||||
WHERE achievement NOT IN (SELECT * FROM lnav_tutorial_current_progress);
|
||||
|
||||
CREATE VIEW lnav_tutorial_log_notification AS
|
||||
SELECT *
|
||||
FROM (SELECT 'org.lnav.tutorial.log' AS id, '["log"]' AS views, jget(value, '/notification') AS message
|
||||
FROM lnav_tutorial_remaining_achievements
|
||||
UNION ALL
|
||||
SELECT 'org.lnav.tutorial.log' AS id,
|
||||
'["log"]' AS views,
|
||||
'Press y to go to the next step in the tutorial' AS message)
|
||||
LIMIT 1;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS lnav_tutorial_progress_listener
|
||||
AFTER INSERT
|
||||
ON lnav_tutorial_progress
|
||||
BEGIN
|
||||
DELETE FROM lnav_user_notifications WHERE id = 'org.lnav.tutorial.log';
|
||||
REPLACE INTO lnav_user_notifications (id, views, message)
|
||||
SELECT *
|
||||
FROM lnav_tutorial_log_notification;
|
||||
END;
|
||||
|
||||
REPLACE INTO lnav_user_notifications (id, views, message)
|
||||
VALUES ('org.lnav.tutorial.text', '["text"]', 'Press "q" to go to the log view')
|
96
docs/tutorials/tutorial1/index.md
Normal file
96
docs/tutorials/tutorial1/index.md
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
name: tutorial1
|
||||
steps:
|
||||
- move-to-error:
|
||||
description: "Move to an error"
|
||||
view_ptr: /top
|
||||
view_value: 6
|
||||
notification: "Press e/Shift+E to move through the errors"
|
||||
comment: |
|
||||
You found the error!
|
||||
[Log formats](https://docs.lnav.org/en/latest/formats.html#format-file-reference)
|
||||
can define the log levels for a given message.
|
||||
The [theme](https://docs.lnav.org/en/latest/config.html#theme-definitions) defines
|
||||
how the levels are displayed.
|
||||
move-to-warning:
|
||||
description: "Move to a warning"
|
||||
notification: "Press w/Shift+W to move through the warnings"
|
||||
view_ptr: /top
|
||||
view_value: 3
|
||||
comment: |
|
||||
You found the warning! The scrollbar on the right is highlighted
|
||||
to show the position of
|
||||
<span class="-lnav_log-level-styles_warning">warnings</span> and
|
||||
<span class="-lnav_log-level-styles_error">errors</span> in this
|
||||
view.
|
||||
- search-for-term:
|
||||
description: "Search for something"
|
||||
notification: "Press / to search for '1AF9...'"
|
||||
view_ptr: /search
|
||||
view_value: 1AF9293A-F42D-4318-BCDF-60234B240955
|
||||
move-to-next-hit:
|
||||
description: "Move to the next hit"
|
||||
notification: "Press n/Shift+N to move through the search hits"
|
||||
view_ptr: /top
|
||||
view_value: 53
|
||||
comment: |
|
||||
The matching text in a search is highlighted in
|
||||
<span class="-lnav_styles_search">reverse-video</span>.
|
||||
However, the text is not always on-screen, so the bar on the
|
||||
left will also be highlighted. You can then press `>` to
|
||||
move right to the next (horizontal) search hit. Pressing
|
||||
`<` will move left to the previous (horizontal) hit or all
|
||||
the way back to the start of the line.
|
||||
move-right:
|
||||
description: "Move to the right"
|
||||
notification: "Press > to move horizontally to view the search hit"
|
||||
view_ptr: /left
|
||||
view_value: 150
|
||||
---
|
||||
# Tutorial 1
|
||||
|
||||
Welcome to the first _interactive_ **lnav** tutorial!
|
||||
|
||||
This tutorial will guide you through the basics of navigating log files.
|
||||
|
||||
## Step 1
|
||||
|
||||
Finding errors quickly is one of the main use-cases for **lnav**. To
|
||||
make that quick and easy, **lnav** parses the log messages in log files
|
||||
as they are loaded and builds indexes of the errors and warnings. You
|
||||
can then use the following hotkeys to jump to them in the log view:
|
||||
|
||||
| Key | Action |
|
||||
|-----------|----------------------------------------------------------------------------------|
|
||||
| `e` | Move to the next <span class="-lnav_log-level-styles_error">error</span> |
|
||||
| `Shift+E` | Move to the previous <span class="-lnav_log-level-styles_error">error</span> |
|
||||
| `w` | Move to the next <span class="-lnav_log-level-styles_warning">warning</span> |
|
||||
| `Shift+W` | Move to the previous <span class="-lnav_log-level-styles_warning">warning</span> |
|
||||
|
||||
To complete this step in the tutorial, you'll need to navigate to the
|
||||
errors and warnings in the sample log file. You can check the upper-right
|
||||
status bar for tips on what you need to do next. Now, press `q` to switch
|
||||
to the log view and begin navigating the sample log file.
|
||||
|
||||
## Step 2
|
||||
|
||||
To search for text in files, you can press `/` to enter the search
|
||||
prompt. To make it easier to search for text that is on-screen, you
|
||||
can press `TAB` to complete values that are shown on screen. For
|
||||
example, to search for the UUID "1AF9293A-F42D-4318-BCDF-60234B240955"
|
||||
that is in one of the error messages, you can enter "1AF9" and then
|
||||
press `TAB` to complete the rest of the UUID.
|
||||
|
||||
Press `q` to switch to the log view and try searching for the UUID.
|
||||
|
||||
## Conclusion
|
||||
|
||||
That's all for now, visit https://lnav.org/downloads to find how to
|
||||
download/install a copy of lnav for your system. The full documentation
|
||||
is available at https://docs.lnav.org.
|
||||
|
||||
## Colophon
|
||||
|
||||
The source for this tutorial is available here:
|
||||
|
||||
https://github.com/tstack/lnav/tree/master/docs/tutorial/tutorial1
|
100
docs/tutorials/tutorial1/tutorial1.glog
Normal file
100
docs/tutorials/tutorial1/tutorial1.glog
Normal file
@ -0,0 +1,100 @@
|
||||
I20170322 06:58:47.082758 61456 blaster.cc:112] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 06:58:58.019562 121 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 06:58:59.059175 123 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
W20170322 06:59:16.062826 61456 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation 1AF9293A-F42D-4318-BCDF-60234B240955 ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 06:59:28.084062 124 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 06:59:32.053551 123 loader.cc:13] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
E20170322 06:59:53.084969 123 loader.cc:552] Excepteur sint occaecat cupidatat non proident 1AF9293A-F42D-4318-BCDF-60234B240955, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:00:00.096693 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:00:03.049849 123 demo.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:00:08.070575 123 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:00:23.019849 123 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:00:28.022692 61457 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:00:29.058438 61456 blaster.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:00:30.028483 123 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:00:49.070676 123 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:00:56.095214 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
W20170322 07:01:14.042785 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:01:31.083704 123 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:01:44.013733 121 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:01:55.024085 121 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:02:02.027811 121 blaster.cc:6782] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:02:14.022939 61456 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:02:30.035925 123 loader.cc:13] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:02:49.024985 123 loader.cc:13] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:03:09.056478 121 blaster.cc:6782] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:03:15.023777 123 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:03:32.066107 123 blaster.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:03:48.028662 124 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:03:54.027078 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:04:09.041478 123 demo.cc:123] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:04:14.068162 121 blaster.cc:6782] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:04:28.099513 124 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:04:40.063473 124 loader.cc:552] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:04:50.024030 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:04:56.081415 121 blaster.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:05:14.096304 123 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:05:21.086331 123 demo.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:05:33.039503 123 loader.cc:13] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:05:43.092657 124 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:05:59.002644 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:06:01.022102 123 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:06:22.005675 123 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:06:37.088974 123 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:06:44.043938 61457 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:06:47.060703 123 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:06:49.052185 61456 demo.cc:123] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:06:52.074424 61457 demo.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:07:02.063191 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:07:10.030327 61457 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:07:11.011338 123 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:07:27.078391 123 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:07:41.061684 123 blaster.cc:112] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:07:53.076558 121 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:08:04.055174 121 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur: 1AF9293A-F42D-4318-BCDF-60234B240955
|
||||
I20170322 07:08:18.046756 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:08:28.004198 123 loader.cc:552] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:08:36.032193 61457 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:08:50.028964 61456 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:08:56.074576 124 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:08:57.090258 123 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:09:00.067690 121 blaster.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:09:19.036483 61457 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:09:40.048046 123 blaster.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:09:52.051526 123 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:10:11.003845 61456 loader.cc:552] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:10:27.094133 123 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:10:43.027892 121 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:10:57.078489 124 demo.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:11:09.014685 123 demo.cc:123] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:11:18.029203 61456 blaster.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:11:24.067068 121 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:11:38.053891 61456 loader.cc:552] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:11:59.027292 61457 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:12:10.069054 61457 loader.cc:13] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:12:22.018053 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:12:39.000436 123 demo.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:12:53.009916 123 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:13:13.051890 121 demo.cc:123] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:13:24.076724 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:13:34.075980 123 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:13:35.096130 61456 blaster.cc:6782] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:13:49.087790 121 demo.cc:123] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:14:08.033671 61457 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:14:23.091358 61456 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:14:35.088133 61456 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:14:55.005577 123 blaster.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:14:58.008392 61457 demo.cc:123] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:15:05.004789 123 loader.cc:552] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:15:07.070013 123 blaster.cc:6782] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:15:08.012805 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:15:25.042509 61456 loader.cc:552] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
E20170322 07:15:32.027688 123 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:15:41.020299 61456 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:15:42.021039 124 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:15:59.063918 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
I20170322 07:16:19.082250 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:16:20.026445 61457 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
I20170322 07:16:41.048447 123 blaster.cc:6782] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
I20170322 07:16:52.097215 61456 demo.cc:123] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
I20170322 07:17:01.020663 61456 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
@ -406,6 +406,7 @@ add_library(
|
||||
sequence_matcher.cc
|
||||
shlex.cc
|
||||
sqlite-extension-func.cc
|
||||
static_file_vtab.cc
|
||||
statusview_curses.cc
|
||||
string-extension-functions.cc
|
||||
sysclip.cc
|
||||
@ -435,6 +436,7 @@ add_library(
|
||||
xml_util.cc
|
||||
xpath_vtab.cc
|
||||
xterm_mouse.cc
|
||||
yaml-extension-functions.cc
|
||||
third-party/md4c/md4c.c
|
||||
third-party/sqlite/ext/series.c
|
||||
third-party/sqlite/ext/dbdump.c
|
||||
@ -525,6 +527,7 @@ add_library(
|
||||
sqlitepp.hh
|
||||
sql_help.hh
|
||||
sql_util.hh
|
||||
static_file_vtab.hh
|
||||
strong_int.hh
|
||||
sysclip.hh
|
||||
sysclip.cfg.hh
|
||||
@ -591,7 +594,10 @@ add_library(
|
||||
set(lnav_SRCS lnav.cc)
|
||||
|
||||
target_include_directories(diag PUBLIC . fmtlib ${CMAKE_CURRENT_BINARY_DIR}
|
||||
third-party third-party/base64/include)
|
||||
third-party
|
||||
third-party/base64/include
|
||||
third-party/rapidyaml
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
diag
|
||||
|
@ -122,6 +122,7 @@ AM_CPPFLAGS = \
|
||||
-I$(srcdir)/fmtlib \
|
||||
-I$(srcdir)/third-party \
|
||||
-I$(srcdir)/third-party/base64/include \
|
||||
-I$(srcdir)/third-party/rapidyaml \
|
||||
-I$(top_srcdir)/src/third-party/scnlib/include \
|
||||
-Wall \
|
||||
$(CODE_COVERAGE_CPPFLAGS) \
|
||||
@ -284,6 +285,7 @@ noinst_HEADERS = \
|
||||
sql_help.hh \
|
||||
sql_util.hh \
|
||||
sqlite-extension-func.hh \
|
||||
static_file_vtab.hh \
|
||||
styling.hh \
|
||||
statusview_curses.hh \
|
||||
strong_int.hh \
|
||||
@ -436,6 +438,7 @@ libdiag_a_SOURCES = \
|
||||
spectro_source.cc \
|
||||
sqlitepp.cc \
|
||||
sqlite-extension-func.cc \
|
||||
static_file_vtab.cc \
|
||||
statusview_curses.cc \
|
||||
string-extension-functions.cc \
|
||||
styling.cc \
|
||||
@ -461,6 +464,7 @@ libdiag_a_SOURCES = \
|
||||
xml_util.cc \
|
||||
xpath_vtab.cc \
|
||||
xterm_mouse.cc \
|
||||
yaml-extension-functions.cc \
|
||||
spookyhash/SpookyV2.cpp
|
||||
|
||||
PLUGIN_SRCS = \
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <assert.h>
|
||||
@ -238,10 +239,15 @@ public:
|
||||
|
||||
const char* begin() const { return this->ab_buffer; }
|
||||
|
||||
void push_back(char ch)
|
||||
auto_buffer& push_back(char ch)
|
||||
{
|
||||
if (this->ab_size == this->ab_capacity) {
|
||||
this->expand_by(256);
|
||||
}
|
||||
this->ab_buffer[this->ab_size] = ch;
|
||||
this->ab_size += 1;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void pop_back() { this->ab_size -= 1; }
|
||||
@ -370,6 +376,8 @@ public:
|
||||
this->expand_to(this->ab_capacity + amount);
|
||||
}
|
||||
|
||||
std::string to_string() const { return {this->ab_buffer, this->ab_size}; }
|
||||
|
||||
private:
|
||||
auto_buffer(char* buffer, size_t capacity)
|
||||
: ab_buffer(buffer), ab_capacity(capacity)
|
||||
|
@ -266,8 +266,11 @@ struct string_fragment {
|
||||
template<typename P>
|
||||
string_fragment find_left_boundary(size_t start, P&& predicate) const
|
||||
{
|
||||
assert((int) start < this->length());
|
||||
assert((int) start <= this->length());
|
||||
|
||||
if (start > 0 && start == this->length()) {
|
||||
start -= 1;
|
||||
}
|
||||
while (start > 0) {
|
||||
if (predicate(this->data()[start])) {
|
||||
start += 1;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
/*
|
||||
Mathieu Stefani, 03 mai 2016
|
||||
|
||||
|
||||
This header provides a Result type that can be used to replace exceptions in code
|
||||
that has to handle error.
|
||||
|
||||
@ -774,7 +774,7 @@ struct Result {
|
||||
{
|
||||
if (!isOk()) {
|
||||
::fprintf(stderr, "%s\n", str);
|
||||
std::terminate();
|
||||
abort();
|
||||
}
|
||||
return expect_impl(std::is_same<T, void>());
|
||||
}
|
||||
@ -887,7 +887,7 @@ struct Result {
|
||||
}
|
||||
|
||||
::fprintf(stderr, "Attempting to unwrap an error Result\n");
|
||||
std::terminate();
|
||||
abort();
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
@ -901,7 +901,7 @@ struct Result {
|
||||
}
|
||||
|
||||
::fprintf(stderr, "Attempting to unwrap an error Result\n");
|
||||
std::terminate();
|
||||
abort();
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
@ -913,7 +913,7 @@ struct Result {
|
||||
}
|
||||
|
||||
::fprintf(stderr, "Attempting to unwrap an error Result\n");
|
||||
std::terminate();
|
||||
abort();
|
||||
}
|
||||
|
||||
E unwrapErr() const
|
||||
@ -923,7 +923,7 @@ struct Result {
|
||||
}
|
||||
|
||||
::fprintf(stderr, "Attempting to unwrapErr an ok Result\n");
|
||||
std::terminate();
|
||||
abort();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -57,19 +57,46 @@ scrub_to_utf8(char* buffer, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
unquote(char* dst, const char* str, size_t len)
|
||||
void
|
||||
quote_content(auto_buffer& buf, const string_fragment& sf, char quote_char)
|
||||
{
|
||||
if (str[0] == 'r' || str[0] == 'u') {
|
||||
str += 1;
|
||||
len -= 1;
|
||||
for (char ch : sf) {
|
||||
if (ch == quote_char) {
|
||||
buf.push_back('\\').push_back(ch);
|
||||
continue;
|
||||
}
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
buf.push_back('\\').push_back('\\');
|
||||
break;
|
||||
case '\n':
|
||||
buf.push_back('\\').push_back('n');
|
||||
break;
|
||||
case '\t':
|
||||
buf.push_back('\\').push_back('t');
|
||||
break;
|
||||
case '\r':
|
||||
buf.push_back('\\').push_back('r');
|
||||
break;
|
||||
case '\a':
|
||||
buf.push_back('\\').push_back('a');
|
||||
break;
|
||||
case '\b':
|
||||
buf.push_back('\\').push_back('b');
|
||||
break;
|
||||
default:
|
||||
buf.push_back(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
char quote_char = str[0];
|
||||
}
|
||||
|
||||
size_t
|
||||
unquote_content(char* dst, const char* str, size_t len, char quote_char)
|
||||
{
|
||||
size_t index = 0;
|
||||
|
||||
require(str[0] == '\'' || str[0] == '"');
|
||||
|
||||
for (size_t lpc = 1; lpc < (len - 1); lpc++, index++) {
|
||||
for (size_t lpc = 0; lpc < len; lpc++, index++) {
|
||||
dst[index] = str[lpc];
|
||||
if (str[lpc] == quote_char) {
|
||||
lpc += 1;
|
||||
@ -96,6 +123,20 @@ unquote(char* dst, const char* str, size_t len)
|
||||
return index;
|
||||
}
|
||||
|
||||
size_t
|
||||
unquote(char* dst, const char* str, size_t len)
|
||||
{
|
||||
if (str[0] == 'r' || str[0] == 'u') {
|
||||
str += 1;
|
||||
len -= 1;
|
||||
}
|
||||
char quote_char = str[0];
|
||||
|
||||
require(str[0] == '\'' || str[0] == '"');
|
||||
|
||||
return unquote_content(dst, &str[1], len - 2, quote_char);
|
||||
}
|
||||
|
||||
size_t
|
||||
unquote_w3c(char* dst, const char* str, size_t len)
|
||||
{
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "auto_mem.hh"
|
||||
#include "intern_string.hh"
|
||||
#include "ww898/cp_utf8.hpp"
|
||||
|
||||
void scrub_to_utf8(char* buffer, size_t length);
|
||||
@ -45,6 +47,12 @@ is_line_ending(char ch)
|
||||
return ch == '\r' || ch == '\n';
|
||||
}
|
||||
|
||||
void quote_content(auto_buffer& buf,
|
||||
const string_fragment& sf,
|
||||
char quote_char);
|
||||
|
||||
size_t unquote_content(char* dst, const char* str, size_t len, char quote_char);
|
||||
|
||||
size_t unquote(char* dst, const char* str, size_t len);
|
||||
|
||||
size_t unquote_w3c(char* dst, const char* str, size_t len);
|
||||
|
@ -63,48 +63,6 @@ SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format
|
||||
ORDER BY total DESC
|
||||
)";
|
||||
|
||||
struct bind_visitor {
|
||||
bind_visitor(sqlite3_stmt* stmt, int index) : bv_stmt(stmt), bv_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const std::string& str) const
|
||||
{
|
||||
sqlite3_bind_text(this->bv_stmt,
|
||||
this->bv_index,
|
||||
str.c_str(),
|
||||
str.size(),
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
void operator()(const string_fragment& str) const
|
||||
{
|
||||
sqlite3_bind_text(this->bv_stmt,
|
||||
this->bv_index,
|
||||
str.data(),
|
||||
str.length(),
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
void operator()(null_value_t) const
|
||||
{
|
||||
sqlite3_bind_null(this->bv_stmt, this->bv_index);
|
||||
}
|
||||
|
||||
void operator()(int64_t value) const
|
||||
{
|
||||
sqlite3_bind_int64(this->bv_stmt, this->bv_index, value);
|
||||
}
|
||||
|
||||
void operator()(double value) const
|
||||
{
|
||||
sqlite3_bind_double(this->bv_stmt, this->bv_index, value);
|
||||
}
|
||||
|
||||
sqlite3_stmt* bv_stmt;
|
||||
int bv_index;
|
||||
};
|
||||
|
||||
int
|
||||
sql_progress(const struct log_cursor& lc)
|
||||
{
|
||||
@ -272,12 +230,12 @@ bind_sql_parameters(exec_context& ec, sqlite3_stmt* stmt)
|
||||
}
|
||||
|
||||
if ((local_var = lvars.find(&name[1])) != lvars.end()) {
|
||||
mapbox::util::apply_visitor(bind_visitor(stmt, lpc + 1),
|
||||
local_var->second);
|
||||
mapbox::util::apply_visitor(
|
||||
sqlitepp::bind_visitor(stmt, lpc + 1), local_var->second);
|
||||
retval[name] = local_var->second;
|
||||
} else if ((global_var = gvars.find(&name[1])) != gvars.end()) {
|
||||
mapbox::util::apply_visitor(bind_visitor(stmt, lpc + 1),
|
||||
global_var->second);
|
||||
mapbox::util::apply_visitor(
|
||||
sqlitepp::bind_visitor(stmt, lpc + 1), global_var->second);
|
||||
retval[name] = global_var->second;
|
||||
} else if ((env_value = getenv(&name[1])) != nullptr) {
|
||||
sqlite3_bind_text(stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
|
||||
@ -620,6 +578,9 @@ execute_file_contents(exec_context& ec,
|
||||
line_number += 1;
|
||||
|
||||
if (trim(line.in()).empty()) {
|
||||
if (multiline && cmdline) {
|
||||
cmdline = cmdline.value() + "\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (line[0] == '#') {
|
||||
@ -920,7 +881,7 @@ execute_init_commands(
|
||||
.with_fd(std::move(fd_copy))
|
||||
.with_include_in_session(false)
|
||||
.with_detect_format(false);
|
||||
lnav_data.ld_files_to_front.emplace_back(OUTPUT_NAME, 0);
|
||||
lnav_data.ld_files_to_front.emplace_back(OUTPUT_NAME, 0_vl);
|
||||
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
@ -1063,7 +1024,7 @@ pipe_callback(exec_context& ec, const std::string& cmdline, auto_fd& fd)
|
||||
.with_fd(pp->get_fd())
|
||||
.with_include_in_session(false)
|
||||
.with_detect_format(false);
|
||||
lnav_data.ld_files_to_front.emplace_back(desc, 0);
|
||||
lnav_data.ld_files_to_front.emplace_back(desc, 0_vl);
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(X, "to close the file"));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Generated by re2c 3.0 on Sat Aug 20 21:19:55 2022 */
|
||||
/* Generated by re2c 3.0 on Mon Aug 22 22:00:24 2022 */
|
||||
#line 1 "../../lnav/src/data_scanner_re.re"
|
||||
/**
|
||||
* Copyright (c) 2015, Timothy Stack
|
||||
|
@ -87,6 +87,10 @@ struct hier_node {
|
||||
template<typename F>
|
||||
static void depth_first(hier_node* root, F func)
|
||||
{
|
||||
if (root == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& child : root->hn_children) {
|
||||
depth_first(child.get(), func);
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "config.h"
|
||||
#include "log_format_ext.hh"
|
||||
#include "log_vtab_impl.hh"
|
||||
#include "md2attr_line.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
#include "relative_time.hh"
|
||||
#include "vtab_module.hh"
|
||||
@ -459,20 +460,41 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
|
||||
|
||||
if (!line_meta.bm_comment.empty()) {
|
||||
const auto* lead = line_meta.bm_tags.empty() ? " \u2514 " : " \u251c ";
|
||||
md2attr_line mdal;
|
||||
attr_line_t al;
|
||||
|
||||
al.with_string(lead).append(lnav::roles::comment(line_meta.bm_comment));
|
||||
al.insert(0, filename_width, ' ');
|
||||
if (tc != nullptr) {
|
||||
auto hl = tc->get_highlights();
|
||||
auto hl_iter = hl.find({highlight_source_t::PREVIEW, "search"});
|
||||
|
||||
if (hl_iter != hl.end()) {
|
||||
hl_iter->second.annotate(al, filename_width);
|
||||
}
|
||||
auto parse_res = md4cpp::parse(line_meta.bm_comment, mdal);
|
||||
if (parse_res.isOk()) {
|
||||
al = parse_res.unwrap();
|
||||
} else {
|
||||
log_error("%d: cannot convert comment to markdown: %s",
|
||||
(int) row,
|
||||
parse_res.unwrapErr().c_str());
|
||||
al = line_meta.bm_comment;
|
||||
}
|
||||
|
||||
dst.emplace_back(al);
|
||||
auto comment_lines = al.rtrim().split_lines();
|
||||
for (size_t lpc = 0; lpc < comment_lines.size(); lpc++) {
|
||||
auto& comment_line = comment_lines[lpc];
|
||||
|
||||
if (lpc == 0 && comment_line.empty()) {
|
||||
continue;
|
||||
}
|
||||
comment_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_COMMENT));
|
||||
comment_line.insert(
|
||||
0, lpc == comment_lines.size() - 1 ? lead : " \u2502 ");
|
||||
comment_line.insert(0, filename_width, ' ');
|
||||
if (tc != nullptr) {
|
||||
auto hl = tc->get_highlights();
|
||||
auto hl_iter = hl.find({highlight_source_t::PREVIEW, "search"});
|
||||
|
||||
if (hl_iter != hl.end()) {
|
||||
hl_iter->second.annotate(comment_line, filename_width);
|
||||
}
|
||||
}
|
||||
|
||||
dst.emplace_back(comment_line);
|
||||
}
|
||||
}
|
||||
if (!line_meta.bm_tags.empty()) {
|
||||
attr_line_t al;
|
||||
|
@ -66,6 +66,8 @@ child_poller::poll(file_collection& fc)
|
||||
return child_poll_result_t::ALIVE;
|
||||
},
|
||||
[this, &fc](auto_pid<process_state::finished>& finished) {
|
||||
require(this->cp_finalizer);
|
||||
|
||||
this->cp_finalizer(fc, finished);
|
||||
return child_poll_result_t::FINISHED;
|
||||
});
|
||||
|
@ -88,16 +88,20 @@ public:
|
||||
auto_pid<process_state::finished>&)> finalizer)
|
||||
: cp_child(std::move(child)), cp_finalizer(std::move(finalizer))
|
||||
{
|
||||
ensure(this->cp_finalizer);
|
||||
}
|
||||
|
||||
child_poller(child_poller&& other) noexcept
|
||||
: cp_child(std::move(other.cp_child)),
|
||||
cp_finalizer(std::move(other.cp_finalizer))
|
||||
{
|
||||
ensure(this->cp_finalizer);
|
||||
}
|
||||
|
||||
child_poller& operator=(child_poller&& other) noexcept
|
||||
{
|
||||
require(other.cp_finalizer);
|
||||
|
||||
this->cp_child = std::move(other.cp_child);
|
||||
this->cp_finalizer = std::move(other.cp_finalizer);
|
||||
|
||||
@ -106,6 +110,10 @@ public:
|
||||
|
||||
~child_poller() noexcept = default;
|
||||
|
||||
child_poller(const child_poller&) = delete;
|
||||
|
||||
child_poller& operator=(const child_poller&) = delete;
|
||||
|
||||
child_poll_result_t poll(file_collection& fc);
|
||||
|
||||
private:
|
||||
|
129
src/file_vtab.cc
129
src/file_vtab.cc
@ -63,15 +63,9 @@ CREATE TABLE lnav_file (
|
||||
|
||||
explicit lnav_file(file_collection& fc) : lf_collection(fc) {}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return this->lf_collection.fc_files.begin();
|
||||
}
|
||||
iterator begin() { return this->lf_collection.fc_files.begin(); }
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return this->lf_collection.fc_files.end();
|
||||
}
|
||||
iterator end() { return this->lf_collection.fc_files.end(); }
|
||||
|
||||
int get_column(const cursor& vc, sqlite3_context* ctx, int col)
|
||||
{
|
||||
@ -170,14 +164,14 @@ CREATE TABLE lnav_file (
|
||||
{
|
||||
vt->zErrMsg = sqlite3_mprintf("Rows cannot be deleted from this table");
|
||||
return SQLITE_ERROR;
|
||||
};
|
||||
}
|
||||
|
||||
int insert_row(sqlite3_vtab* tab, sqlite3_int64& rowid_out)
|
||||
{
|
||||
tab->zErrMsg
|
||||
= sqlite3_mprintf("Rows cannot be inserted into this table");
|
||||
return SQLITE_ERROR;
|
||||
};
|
||||
}
|
||||
|
||||
int update_row(sqlite3_vtab* tab,
|
||||
sqlite3_int64& rowid,
|
||||
@ -223,15 +217,128 @@ CREATE TABLE lnav_file (
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
};
|
||||
}
|
||||
|
||||
file_collection& lf_collection;
|
||||
};
|
||||
|
||||
struct lnav_file_metadata {
|
||||
static constexpr const char* NAME = "lnav_file_metadata";
|
||||
static constexpr const char* CREATE_STMT = R"(
|
||||
-- Access the metadata embedded in open files
|
||||
CREATE TABLE lnav_file_metadata (
|
||||
filepath text, -- The path to the file.
|
||||
descriptor text, -- The descriptor that identifies the source of the metadata.
|
||||
mimetype text, -- The MIME type of the metadata.
|
||||
content text -- The metadata itself.
|
||||
);
|
||||
)";
|
||||
|
||||
struct cursor {
|
||||
struct metadata_row {
|
||||
metadata_row(std::shared_ptr<logfile> lf, std::string desc)
|
||||
: mr_logfile(lf), mr_descriptor(std::move(desc))
|
||||
{
|
||||
}
|
||||
std::shared_ptr<logfile> mr_logfile;
|
||||
std::string mr_descriptor;
|
||||
};
|
||||
|
||||
sqlite3_vtab_cursor base;
|
||||
lnav_file_metadata& c_meta;
|
||||
std::vector<metadata_row>::iterator c_iter;
|
||||
std::vector<metadata_row> c_rows;
|
||||
|
||||
cursor(sqlite3_vtab* vt)
|
||||
: base({vt}),
|
||||
c_meta(((vtab_module<lnav_file_metadata>::vtab*) vt)->v_impl)
|
||||
{
|
||||
for (auto& lf : this->c_meta.lfm_collection.fc_files) {
|
||||
auto& lf_meta = lf->get_embedded_metadata();
|
||||
|
||||
for (const auto& meta_pair : lf_meta) {
|
||||
this->c_rows.emplace_back(lf, meta_pair.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~cursor() { this->c_iter = this->c_rows.end(); }
|
||||
|
||||
int next()
|
||||
{
|
||||
if (this->c_iter != this->c_rows.end()) {
|
||||
++this->c_iter;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int eof() { return this->c_iter == this->c_rows.end(); }
|
||||
|
||||
int reset()
|
||||
{
|
||||
this->c_iter = this->c_rows.begin();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int get_rowid(sqlite3_int64& rowid_out)
|
||||
{
|
||||
rowid_out = this->c_iter - this->c_rows.begin();
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
};
|
||||
|
||||
explicit lnav_file_metadata(file_collection& fc) : lfm_collection(fc) {}
|
||||
|
||||
int get_column(const cursor& vc, sqlite3_context* ctx, int col)
|
||||
{
|
||||
auto& mr = *vc.c_iter;
|
||||
|
||||
switch (col) {
|
||||
case 0:
|
||||
to_sqlite(ctx, mr.mr_logfile->get_filename());
|
||||
break;
|
||||
case 1:
|
||||
to_sqlite(ctx, mr.mr_descriptor);
|
||||
break;
|
||||
case 2:
|
||||
to_sqlite(
|
||||
ctx,
|
||||
fmt::to_string(
|
||||
mr.mr_logfile->get_embedded_metadata()[mr.mr_descriptor]
|
||||
.m_format));
|
||||
break;
|
||||
case 3:
|
||||
to_sqlite(
|
||||
ctx,
|
||||
fmt::to_string(
|
||||
mr.mr_logfile->get_embedded_metadata()[mr.mr_descriptor]
|
||||
.m_value));
|
||||
break;
|
||||
default:
|
||||
ensure(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
file_collection& lfm_collection;
|
||||
};
|
||||
|
||||
struct injectable_lnav_file : vtab_module<lnav_file> {
|
||||
using vtab_module<lnav_file>::vtab_module;
|
||||
using injectable = injectable_lnav_file(file_collection&);
|
||||
};
|
||||
|
||||
struct injectable_lnav_file_metadata
|
||||
: vtab_module<tvt_no_update<lnav_file_metadata>> {
|
||||
using vtab_module<tvt_no_update<lnav_file_metadata>>::vtab_module;
|
||||
using injectable = injectable_lnav_file_metadata(file_collection&);
|
||||
};
|
||||
|
||||
static auto file_binder
|
||||
= injector::bind_multiple<vtab_module_base>().add<injectable_lnav_file>();
|
||||
|
||||
static auto file_meta_binder = injector::bind_multiple<vtab_module_base>()
|
||||
.add<injectable_lnav_file_metadata>();
|
||||
|
@ -7,6 +7,9 @@
|
||||
"regex": {
|
||||
"std": {
|
||||
"pattern": "^(?<level>[IWECF])(?<timestamp>\\d{4} \\d{2}:\\d{2}:\\d{2}\\.\\d{6}) +(?<thread>\\d+) (?<src_file>[^:]+):(?<src_line>\\d+)\\] (?<body>.*)"
|
||||
},
|
||||
"std-with-year": {
|
||||
"pattern": "^(?<level>[IWECF])(?<timestamp>\\d{8} \\d{2}:\\d{2}:\\d{2}\\.\\d{6}) +(?<thread>\\d+) (?<src_file>[^:]+):(?<src_line>\\d+)\\] (?<body>.*)"
|
||||
}
|
||||
},
|
||||
"level-field": "level",
|
||||
@ -39,6 +42,10 @@
|
||||
},
|
||||
{
|
||||
"line": "E0517 15:04:22.619632 52992 logging_unittest.cc:253] Log every 3, iteration 19"
|
||||
},
|
||||
{
|
||||
"line": "I20200308 23:47:32.089828 400441 config.cc:27] Loading user configuration: /home/aesophor/.config/wmderland/config",
|
||||
"level": "info"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ CREATE TABLE fstat (
|
||||
cursor(sqlite3_vtab* vt) : base({vt})
|
||||
{
|
||||
memset(&this->c_stat, 0, sizeof(this->c_stat));
|
||||
};
|
||||
}
|
||||
|
||||
void load_stat()
|
||||
{
|
||||
@ -115,7 +115,7 @@ CREATE TABLE fstat (
|
||||
{
|
||||
this->c_path_index += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int next()
|
||||
{
|
||||
@ -124,25 +124,19 @@ CREATE TABLE fstat (
|
||||
this->load_stat();
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
};
|
||||
|
||||
int reset()
|
||||
{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int eof()
|
||||
{
|
||||
return this->c_path_index >= this->c_glob->gl_pathc;
|
||||
};
|
||||
int reset() { return SQLITE_OK; }
|
||||
|
||||
int eof() { return this->c_path_index >= this->c_glob->gl_pathc; }
|
||||
|
||||
int get_rowid(sqlite3_int64& rowid_out)
|
||||
{
|
||||
rowid_out = this->c_path_index;
|
||||
|
||||
return SQLITE_OK;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
int get_column(const cursor& vc, sqlite3_context* ctx, int col)
|
||||
|
@ -107,8 +107,8 @@ down to display the new lines, much like `tail -f`.
|
||||
|
||||
On color displays, the lines will be highlighted as follows:
|
||||
|
||||
* Errors will be colored in ${ansi_red}red${ansi_norm};
|
||||
* warnings will be ${ansi_yellow}yellow${ansi_norm};
|
||||
* Errors will be colored in <span class="-lnav_log-level-styles_error">red</span>;
|
||||
* warnings will be <span class="-lnav_log-level-styles_warning">yellow</span>;
|
||||
* boundaries between days will be ${ansi_underline}underlined${ansi_norm}; and
|
||||
* various color highlights will be applied to: IP addresses, SQL keywords,
|
||||
XML tags, file and line numbers in Java backtraces, and quoted strings.
|
||||
|
@ -100,12 +100,20 @@ highlighter::annotate_capture(attr_line_t& al, const line_range& lr) const
|
||||
void
|
||||
highlighter::annotate(attr_line_t& al, int start) const
|
||||
{
|
||||
if (!this->h_regex) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& vc = view_colors::singleton();
|
||||
const auto& str = al.get_string();
|
||||
auto& sa = al.get_attrs();
|
||||
auto sf = string_fragment::from_str_range(
|
||||
str, start, std::min(size_t{8192}, str.size()));
|
||||
|
||||
if (!sf.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pcre_context_static<60> pc;
|
||||
pcre_input pi(sf);
|
||||
|
||||
|
@ -162,6 +162,8 @@ handle_keyseq(const char* keyseq)
|
||||
auto& var_stack = ec.ec_local_vars;
|
||||
|
||||
ec.ec_global_vars = lnav_data.ld_exec_context.ec_global_vars;
|
||||
ec.ec_error_callback_stack
|
||||
= lnav_data.ld_exec_context.ec_error_callback_stack;
|
||||
var_stack.push(std::map<std::string, scoped_value_t>());
|
||||
auto& vars = var_stack.top();
|
||||
vars["keyseq"] = keyseq;
|
||||
@ -174,11 +176,7 @@ handle_keyseq(const char* keyseq)
|
||||
} else {
|
||||
auto um = result.unwrapErr();
|
||||
|
||||
um.um_snippets.clear();
|
||||
um.um_reason.clear();
|
||||
um.um_notes.clear();
|
||||
um.um_help.clear();
|
||||
lnav_data.ld_rl_view->set_attr_value(um.to_attr_line());
|
||||
ec.ec_error_callback_stack.back()(um);
|
||||
}
|
||||
|
||||
if (!kc.kc_alt_msg.empty()) {
|
||||
@ -295,12 +293,10 @@ handle_paging_key(int ch)
|
||||
break;
|
||||
|
||||
case '>': {
|
||||
std::pair<int, int> range;
|
||||
|
||||
tc->horiz_shift(
|
||||
tc->get_top(), tc->get_bottom(), tc->get_left(), range);
|
||||
if (range.second != INT_MAX) {
|
||||
tc->set_left(range.second);
|
||||
auto range_opt = tc->horiz_shift(
|
||||
tc->get_top(), tc->get_bottom(), tc->get_left());
|
||||
if (range_opt && range_opt.value().second != INT_MAX) {
|
||||
tc->set_left(range_opt.value().second);
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
HELP_MSG_1(m, "to bookmark a line"));
|
||||
} else {
|
||||
@ -312,12 +308,10 @@ handle_paging_key(int ch)
|
||||
if (tc->get_left() == 0) {
|
||||
alerter::singleton().chime("no more search hits to the left");
|
||||
} else {
|
||||
std::pair<int, int> range;
|
||||
|
||||
tc->horiz_shift(
|
||||
tc->get_top(), tc->get_bottom(), tc->get_left(), range);
|
||||
if (range.first != -1) {
|
||||
tc->set_left(range.first);
|
||||
auto range_opt = tc->horiz_shift(
|
||||
tc->get_top(), tc->get_bottom(), tc->get_left());
|
||||
if (range_opt && range_opt.value().first != -1) {
|
||||
tc->set_left(range_opt.value().first);
|
||||
} else {
|
||||
tc->set_left(0);
|
||||
}
|
||||
@ -395,8 +389,7 @@ handle_paging_key(int ch)
|
||||
tc->shift_top(1_vl);
|
||||
}
|
||||
if (lnav_data.ld_last_user_mark[tc] + 1
|
||||
>= tc->get_inner_height())
|
||||
{
|
||||
>= tc->get_inner_height()) {
|
||||
break;
|
||||
}
|
||||
lnav_data.ld_last_user_mark[tc] += 1;
|
||||
@ -442,8 +435,7 @@ handle_paging_key(int ch)
|
||||
|
||||
case 'M':
|
||||
if (lnav_data.ld_last_user_mark.find(tc)
|
||||
== lnav_data.ld_last_user_mark.end())
|
||||
{
|
||||
== lnav_data.ld_last_user_mark.end()) {
|
||||
alerter::singleton().chime("no lines have been marked");
|
||||
} else {
|
||||
int start_line = std::min((int) tc->get_top(),
|
||||
@ -459,20 +451,20 @@ handle_paging_key(int ch)
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case 'S':
|
||||
{
|
||||
bookmark_vector<vis_line_t>::iterator iter;
|
||||
case 'S':
|
||||
{
|
||||
bookmark_vector<vis_line_t>::iterator iter;
|
||||
|
||||
for (iter = bm[&textview_curses::BM_SEARCH].begin();
|
||||
iter != bm[&textview_curses::BM_SEARCH].end();
|
||||
++iter) {
|
||||
tc->toggle_user_mark(&textview_curses::BM_USER, *iter);
|
||||
}
|
||||
for (iter = bm[&textview_curses::BM_SEARCH].begin();
|
||||
iter != bm[&textview_curses::BM_SEARCH].end();
|
||||
++iter) {
|
||||
tc->toggle_user_mark(&textview_curses::BM_USER, *iter);
|
||||
}
|
||||
|
||||
lnav_data.ld_last_user_mark[tc] = -1;
|
||||
tc->reload_data();
|
||||
}
|
||||
break;
|
||||
lnav_data.ld_last_user_mark[tc] = -1;
|
||||
tc->reload_data();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 's':
|
||||
@ -487,8 +479,7 @@ handle_paging_key(int ch)
|
||||
while (next_top < tc->get_inner_height()) {
|
||||
if (!lss->find_line(lss->at(next_top))->is_message()) {
|
||||
} else if (lss->get_line_accel_direction(next_top)
|
||||
== log_accel::A_DECEL)
|
||||
{
|
||||
== log_accel::A_DECEL) {
|
||||
--next_top;
|
||||
tc->set_top(next_top);
|
||||
break;
|
||||
@ -511,8 +502,7 @@ handle_paging_key(int ch)
|
||||
while (0 <= next_top && next_top < tc->get_inner_height()) {
|
||||
if (!lss->find_line(lss->at(next_top))->is_message()) {
|
||||
} else if (lss->get_line_accel_direction(next_top)
|
||||
== log_accel::A_DECEL)
|
||||
{
|
||||
== log_accel::A_DECEL) {
|
||||
--next_top;
|
||||
tc->set_top(next_top);
|
||||
break;
|
||||
@ -625,8 +615,7 @@ handle_paging_key(int ch)
|
||||
while (true) {
|
||||
if (ch == 'o') {
|
||||
if (++next_helper.lh_current_line
|
||||
>= tc->get_inner_height())
|
||||
{
|
||||
>= tc->get_inner_height()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@ -782,8 +771,7 @@ handle_paging_key(int ch)
|
||||
for (row = 0; row < dls.dls_rows.size(); row++) {
|
||||
if (strcmp(dls.dls_rows[row][log_line_index.value()],
|
||||
linestr.data())
|
||||
== 0)
|
||||
{
|
||||
== 0) {
|
||||
vis_line_t db_line(row);
|
||||
|
||||
db_tc->set_top(db_line);
|
||||
@ -821,8 +809,7 @@ handle_paging_key(int ch)
|
||||
size_t col_len = strlen(col_value);
|
||||
|
||||
if (dts.scan(col_value, col_len, nullptr, &tm, tv)
|
||||
!= nullptr)
|
||||
{
|
||||
!= nullptr) {
|
||||
lnav_data.ld_log_source.find_from_time(tv) |
|
||||
[tc](auto vl) {
|
||||
tc->set_top(vl);
|
||||
|
@ -161,7 +161,7 @@
|
||||
:comment *text*
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Attach a comment to the top log line
|
||||
Attach a comment to the top log line. The comment will be displayed right below the log message it is associated with. The comment can be formatted using markdown and you can add new-lines with '\n'.
|
||||
|
||||
**Parameters**
|
||||
* **text\*** --- The comment text
|
||||
@ -571,13 +571,13 @@
|
||||
|
||||
.. _goto:
|
||||
|
||||
:goto *line#|N%|timestamp*
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
:goto *line#|N%|timestamp|#anchor*
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Go to the given location in the top view
|
||||
|
||||
**Parameters**
|
||||
* **line#|N%|timestamp\*** --- A line number, percent into the file, or a timestamp
|
||||
* **line#|N%|timestamp|#anchor\*** --- A line number, percent into the file, timestamp, or an anchor in a text file
|
||||
|
||||
**Examples**
|
||||
To go to line 22:
|
||||
@ -598,6 +598,12 @@
|
||||
|
||||
:goto 2017-01-01
|
||||
|
||||
To go to the Screenshots section:
|
||||
|
||||
.. code-block:: lnav
|
||||
|
||||
:goto #screenshots
|
||||
|
||||
**See Also**
|
||||
:ref:`next_location`, :ref:`next_mark`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`relative_goto`
|
||||
|
||||
|
@ -1626,7 +1626,7 @@ jget(*json*, *ptr*, *\[default\]*)
|
||||
Hello
|
||||
|
||||
**See Also**
|
||||
:ref:`json_concat`, :ref:`json_contains`, :ref:`json_group_array`, :ref:`json_group_object`
|
||||
:ref:`json_concat`, :ref:`json_contains`, :ref:`json_group_array`, :ref:`json_group_object`, :ref:`yaml_to_json`
|
||||
|
||||
----
|
||||
|
||||
@ -1710,7 +1710,7 @@ json_concat(*json*, *value*)
|
||||
[1,2,3,4,5]
|
||||
|
||||
**See Also**
|
||||
:ref:`jget`, :ref:`json_contains`, :ref:`json_group_array`, :ref:`json_group_object`
|
||||
:ref:`jget`, :ref:`json_contains`, :ref:`json_group_array`, :ref:`json_group_object`, :ref:`yaml_to_json`
|
||||
|
||||
----
|
||||
|
||||
@ -1742,7 +1742,7 @@ json_contains(*json*, *value*)
|
||||
1
|
||||
|
||||
**See Also**
|
||||
:ref:`jget`, :ref:`json_concat`, :ref:`json_group_array`, :ref:`json_group_object`
|
||||
:ref:`jget`, :ref:`json_concat`, :ref:`json_group_array`, :ref:`json_group_object`, :ref:`yaml_to_json`
|
||||
|
||||
----
|
||||
|
||||
@ -1773,7 +1773,7 @@ json_group_array(*value*)
|
||||
[1,2,3]
|
||||
|
||||
**See Also**
|
||||
:ref:`jget`, :ref:`json_concat`, :ref:`json_contains`, :ref:`json_group_object`
|
||||
:ref:`jget`, :ref:`json_concat`, :ref:`json_contains`, :ref:`json_group_object`, :ref:`yaml_to_json`
|
||||
|
||||
----
|
||||
|
||||
@ -1805,7 +1805,7 @@ json_group_object(*name*, *value*)
|
||||
{"a":1,"b":2}
|
||||
|
||||
**See Also**
|
||||
:ref:`jget`, :ref:`json_concat`, :ref:`json_contains`, :ref:`json_group_array`
|
||||
:ref:`jget`, :ref:`json_concat`, :ref:`json_contains`, :ref:`json_group_array`, :ref:`yaml_to_json`
|
||||
|
||||
----
|
||||
|
||||
@ -3731,6 +3731,30 @@ xpath(*xpath*, *xmldoc*)
|
||||
----
|
||||
|
||||
|
||||
.. _yaml_to_json:
|
||||
|
||||
yaml_to_json(*yaml*)
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Convert a YAML document to a JSON-encoded string
|
||||
|
||||
**Parameters**
|
||||
* **yaml\*** --- The YAML value to convert to JSON.
|
||||
|
||||
**Examples**
|
||||
To convert the document "abc: def":
|
||||
|
||||
.. code-block:: custsqlite
|
||||
|
||||
;SELECT yaml_to_json('abc: def')
|
||||
{"abc": "def"}
|
||||
|
||||
**See Also**
|
||||
:ref:`jget`, :ref:`json_concat`, :ref:`json_contains`, :ref:`json_group_array`, :ref:`json_group_object`
|
||||
|
||||
----
|
||||
|
||||
|
||||
.. _zeroblob:
|
||||
|
||||
zeroblob(*N*)
|
||||
|
138
src/lnav.cc
138
src/lnav.cc
@ -118,6 +118,7 @@
|
||||
#include "readline_curses.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
#include "regexp_vtab.hh"
|
||||
#include "scn/scn.h"
|
||||
#include "service_tags.hh"
|
||||
#include "session_data.hh"
|
||||
#include "spectro_source.hh"
|
||||
@ -125,6 +126,7 @@
|
||||
#include "sql_util.hh"
|
||||
#include "sqlite-extension-func.hh"
|
||||
#include "sqlitepp.client.hh"
|
||||
#include "static_file_vtab.hh"
|
||||
#include "tailer/tailer.looper.hh"
|
||||
#include "term_extra.hh"
|
||||
#include "termios_guard.hh"
|
||||
@ -1079,6 +1081,60 @@ looper()
|
||||
lnav_data.ld_rl_view->add_possibility(
|
||||
ln_mode_t::COMMAND, "levelname", level_names);
|
||||
|
||||
auto echo_views_stmt_res = prepare_stmt(lnav_data.ld_db,
|
||||
#if SQLITE_VERSION_NUMBER < 3033000
|
||||
R"(
|
||||
UPDATE lnav_views_echo
|
||||
SET top = (SELECT top FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
left = (SELECT left FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
height = (SELECT height FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
inner_height = (SELECT inner_height FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
top_time = (SELECT top_time FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
search = (SELECT search FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name)
|
||||
WHERE EXISTS (SELECT * FROM lnav_views WHERE name = lnav_views_echo.name AND
|
||||
(
|
||||
lnav_views.top != lnav_views_echo.top OR
|
||||
lnav_views.left != lnav_views_echo.left OR
|
||||
lnav_views.height != lnav_views_echo.height OR
|
||||
lnav_views.inner_height != lnav_views_echo.inner_height OR
|
||||
lnav_views.top_time != lnav_views_echo.top_time OR
|
||||
lnav_views.search != lnav_views_echo.search
|
||||
))
|
||||
)"
|
||||
#else
|
||||
R"(
|
||||
UPDATE lnav_views_echo
|
||||
SET top = orig.top,
|
||||
left = orig.left,
|
||||
height = orig.height,
|
||||
inner_height = orig.inner_height,
|
||||
top_time = orig.top_time,
|
||||
search = orig.search
|
||||
FROM (SELECT * FROM lnav_views) AS orig
|
||||
WHERE orig.name = lnav_views_echo.name AND
|
||||
(
|
||||
orig.top != lnav_views_echo.top OR
|
||||
orig.left != lnav_views_echo.left OR
|
||||
orig.height != lnav_views_echo.height OR
|
||||
orig.inner_height != lnav_views_echo.inner_height OR
|
||||
orig.top_time != lnav_views_echo.top_time OR
|
||||
orig.search != lnav_views_echo.search
|
||||
)
|
||||
)"
|
||||
#endif
|
||||
);
|
||||
|
||||
if (echo_views_stmt_res.isErr()) {
|
||||
lnav::console::print(
|
||||
stderr,
|
||||
lnav::console::user_message::error(
|
||||
"unable to prepare UPDATE statement for lnav_views_echo "
|
||||
"table")
|
||||
.with_reason(echo_views_stmt_res.unwrapErr()));
|
||||
return;
|
||||
}
|
||||
auto echo_views_stmt = echo_views_stmt_res.unwrap();
|
||||
|
||||
(void) signal(SIGINT, sigint);
|
||||
(void) signal(SIGTERM, sigint);
|
||||
(void) signal(SIGWINCH, sigwinch);
|
||||
@ -1370,33 +1426,6 @@ looper()
|
||||
auto next_status_update_time = next_rebuild_time;
|
||||
auto next_rescan_time = next_rebuild_time;
|
||||
|
||||
auto echo_views_stmt = prepare_stmt(lnav_data.ld_db,
|
||||
#if SQLITE_VERSION_NUMBER < 3033000
|
||||
R"(
|
||||
UPDATE lnav_views_echo
|
||||
SET top = (SELECT top FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
left = (SELECT left FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
height = (SELECT height FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
inner_height = (SELECT inner_height FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
top_time = (SELECT top_time FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name),
|
||||
search = (SELECT search FROM lnav_views WHERE lnav_views.name = lnav_views_echo.name)
|
||||
)"
|
||||
#else
|
||||
R"(
|
||||
UPDATE lnav_views_echo
|
||||
SET top = orig.top,
|
||||
left = orig.left,
|
||||
height = orig.height,
|
||||
inner_height = orig.inner_height,
|
||||
top_time = orig.top_time,
|
||||
search = orig.search
|
||||
FROM (SELECT * FROM lnav_views) AS orig
|
||||
WHERE orig.name = lnav_views_echo.name
|
||||
)"
|
||||
#endif
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
while (lnav_data.ld_looping) {
|
||||
auto loop_deadline
|
||||
= ui_clock::now() + (session_stage == 0 ? 3s : 50ms);
|
||||
@ -1723,7 +1752,6 @@ UPDATE lnav_views_echo
|
||||
&& lnav_data.ld_text_source.text_line_count() > 0)
|
||||
{
|
||||
ensure_view(&lnav_data.ld_views[LNV_TEXT]);
|
||||
lnav_data.ld_views[LNV_TEXT].set_top(0_vl);
|
||||
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
|
||||
f, F, "to switch to the next/previous file"));
|
||||
}
|
||||
@ -1811,8 +1839,12 @@ UPDATE lnav_views_echo
|
||||
{
|
||||
const auto& vs
|
||||
= session_data.sd_view_states[view_index];
|
||||
auto& tview = lnav_data.ld_views[view_index];
|
||||
|
||||
if (vs.vs_top > 0) {
|
||||
if (vs.vs_top > 0 && tview.get_top() == 0_vl) {
|
||||
log_info("restoring %s view top: %d",
|
||||
lnav_view_strings[view_index],
|
||||
vs.vs_top);
|
||||
lnav_data.ld_views[view_index].set_top(
|
||||
vis_line_t(vs.vs_top));
|
||||
}
|
||||
@ -1820,7 +1852,7 @@ UPDATE lnav_views_echo
|
||||
if (lnav_data.ld_mode == ln_mode_t::FILES) {
|
||||
if (lnav_data.ld_active_files.fc_name_to_errors.empty())
|
||||
{
|
||||
log_debug("switching to paging!");
|
||||
log_info("switching to paging!");
|
||||
lnav_data.ld_mode = ln_mode_t::PAGING;
|
||||
lnav_data.ld_active_files.fc_files
|
||||
| lnav::itertools::for_each(
|
||||
@ -2017,6 +2049,7 @@ main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
register_environ_vtab(lnav_data.ld_db.in());
|
||||
register_static_file_vtab(lnav_data.ld_db.in());
|
||||
{
|
||||
static auto vtab_modules
|
||||
= injector::get<std::vector<std::shared_ptr<vtab_module_base>>>();
|
||||
@ -2476,7 +2509,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
lnav_data.ld_pipers.push_back(pp);
|
||||
lnav_data.ld_active_files.fc_file_names[desc].with_fd(
|
||||
pp->get_fd());
|
||||
lnav_data.ld_files_to_front.template emplace_back(desc, 0);
|
||||
lnav_data.ld_files_to_front.template emplace_back(desc, 0_vl);
|
||||
}))
|
||||
.add_input_delegate(lnav_data.ld_log_source)
|
||||
.set_tail_space(2_vl)
|
||||
@ -2557,8 +2590,10 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
}
|
||||
}
|
||||
|
||||
load_format_extra(
|
||||
lnav_data.ld_db.in(), lnav_data.ld_config_paths, loader_errors);
|
||||
load_format_extra(lnav_data.ld_db.in(),
|
||||
ec.ec_global_vars,
|
||||
lnav_data.ld_config_paths,
|
||||
loader_errors);
|
||||
load_format_vtabs(lnav_data.ld_vtab_manager.get(), loader_errors);
|
||||
|
||||
if (!loader_errors.empty()) {
|
||||
@ -2622,9 +2657,36 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
}
|
||||
|
||||
for (auto& file_path : file_args) {
|
||||
auto file_path_without_trailer = file_path;
|
||||
auto file_loc = file_location_t{mapbox::util::no_init{}};
|
||||
auto_mem<char> abspath;
|
||||
struct stat st;
|
||||
|
||||
auto colon_index = file_path.rfind(':');
|
||||
if (colon_index != std::string::npos) {
|
||||
file_path_without_trailer = file_path.substr(0, colon_index);
|
||||
auto top_range = scn::string_view{&file_path[colon_index + 1],
|
||||
&(*file_path.cend())};
|
||||
auto scan_res = scn::scan_value<int>(top_range);
|
||||
|
||||
if (scan_res) {
|
||||
file_path_without_trailer = file_path.substr(0, colon_index);
|
||||
file_loc = vis_line_t(scan_res.value());
|
||||
} else {
|
||||
log_warning(
|
||||
"failed to parse line number from file path with colon: %s",
|
||||
file_path.c_str());
|
||||
}
|
||||
}
|
||||
auto hash_index = file_path.rfind('#');
|
||||
if (hash_index != std::string::npos) {
|
||||
file_loc = file_path.substr(hash_index);
|
||||
file_path_without_trailer = file_path.substr(0, hash_index);
|
||||
}
|
||||
if (stat(file_path_without_trailer.c_str(), &st) == 0) {
|
||||
file_path = file_path_without_trailer;
|
||||
}
|
||||
|
||||
if (file_path == "-") {
|
||||
load_stdin = true;
|
||||
}
|
||||
@ -2711,6 +2773,10 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
} else {
|
||||
lnav_data.ld_active_files.fc_file_names.emplace(
|
||||
abspath.in(), logfile_open_options());
|
||||
if (file_loc.valid()) {
|
||||
lnav_data.ld_files_to_front.emplace_back(abspath.in(),
|
||||
file_loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2933,13 +2999,13 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
|
||||
log_tc->set_top(0_vl);
|
||||
text_tc = &lnav_data.ld_views[LNV_TEXT];
|
||||
text_tc->set_top(0_vl);
|
||||
text_tc->set_height(vis_line_t(text_tc->get_inner_height()));
|
||||
text_tc->set_height(vis_line_t(text_tc->get_inner_height()
|
||||
- text_tc->get_top()));
|
||||
setup_highlights(lnav_data.ld_views[LNV_TEXT].get_highlights());
|
||||
if (lnav_data.ld_log_source.text_line_count() == 0
|
||||
&& lnav_data.ld_text_source.text_line_count() > 0)
|
||||
{
|
||||
toggle_view(&lnav_data.ld_views[LNV_TEXT]);
|
||||
ensure_view(&lnav_data.ld_views[LNV_TEXT]);
|
||||
}
|
||||
|
||||
log_info("Executing initial commands");
|
||||
|
@ -158,6 +158,8 @@ struct key_repeat_history {
|
||||
};
|
||||
};
|
||||
|
||||
using file_location_t = mapbox::util::variant<vis_line_t, std::string>;
|
||||
|
||||
struct lnav_data_t {
|
||||
std::map<std::string, std::list<session_pair_t>> ld_session_id;
|
||||
time_t ld_session_time;
|
||||
@ -171,7 +173,7 @@ struct lnav_data_t {
|
||||
std::vector<ghc::filesystem::path> ld_config_paths;
|
||||
file_collection ld_active_files;
|
||||
std::list<child_poller> ld_child_pollers;
|
||||
std::list<std::pair<std::string, int>> ld_files_to_front;
|
||||
std::list<std::pair<std::string, file_location_t>> ld_files_to_front;
|
||||
bool ld_stdout_used;
|
||||
sig_atomic_t ld_looping;
|
||||
sig_atomic_t ld_winched;
|
||||
@ -258,8 +260,7 @@ struct lnav_data_t {
|
||||
bool ld_show_help_view{false};
|
||||
};
|
||||
|
||||
struct static_service {
|
||||
};
|
||||
struct static_service {};
|
||||
|
||||
class main_looper
|
||||
: public isc::service<main_looper>
|
||||
|
@ -150,8 +150,8 @@ public:
|
||||
|
||||
auto iter = session_data.sd_file_states.find(lf->get_filename());
|
||||
if (iter != session_data.sd_file_states.end()) {
|
||||
log_debug("found state for log file %d",
|
||||
iter->second.fs_is_visible);
|
||||
log_info(" found visibility state for log file: %d",
|
||||
iter->second.fs_is_visible);
|
||||
|
||||
lnav_data.ld_log_source.find_data(lf) | [&iter](auto ld) {
|
||||
ld->set_visibility(iter->second.fs_is_visible);
|
||||
@ -181,7 +181,7 @@ public:
|
||||
}
|
||||
|
||||
std::shared_ptr<logfile> front_file;
|
||||
int front_top{-1};
|
||||
file_location_t front_top;
|
||||
bool did_promotion{false};
|
||||
};
|
||||
|
||||
@ -219,12 +219,35 @@ rebuild_indexes(nonstd::optional<ui_clock::time_point> deadline)
|
||||
old_bottoms[LNV_TEXT] = -1_vl;
|
||||
}
|
||||
|
||||
if (cb.front_top < 0) {
|
||||
cb.front_top += text_view.get_inner_height();
|
||||
}
|
||||
if (cb.front_top < text_view.get_inner_height()) {
|
||||
text_view.set_top(vis_line_t(cb.front_top));
|
||||
nonstd::optional<vis_line_t> new_top_opt;
|
||||
cb.front_top.match(
|
||||
[&new_top_opt](vis_line_t vl) {
|
||||
log_info("file open request to jump to line: %d", (int) vl);
|
||||
if (vl < 0_vl) {
|
||||
vl += lnav_data.ld_views[LNV_TEXT].get_inner_height();
|
||||
}
|
||||
if (vl < lnav_data.ld_views[LNV_TEXT].get_inner_height()) {
|
||||
new_top_opt = vl;
|
||||
}
|
||||
},
|
||||
[&new_top_opt](const std::string& loc) {
|
||||
log_info("file open request to jump to anchor: %s",
|
||||
loc.c_str());
|
||||
auto* ta = dynamic_cast<text_anchors*>(
|
||||
lnav_data.ld_views[LNV_TEXT].get_sub_source());
|
||||
|
||||
if (ta != nullptr) {
|
||||
new_top_opt = ta->row_for_anchor(loc);
|
||||
}
|
||||
});
|
||||
if (new_top_opt) {
|
||||
log_info(" setting requested top line: %d",
|
||||
(int) new_top_opt.value());
|
||||
text_view.set_top(new_top_opt.value());
|
||||
log_info(" actual top is now: %d", (int) text_view.get_top());
|
||||
scroll_downs[LNV_TEXT] = false;
|
||||
} else {
|
||||
log_warning("could not jump to requested line");
|
||||
}
|
||||
}
|
||||
if (cb.did_promotion && deadline) {
|
||||
@ -364,6 +387,7 @@ update_active_files(file_collection& new_files)
|
||||
lnav_data.ld_active_files.fc_child_pollers.begin()),
|
||||
std::make_move_iterator(
|
||||
lnav_data.ld_active_files.fc_child_pollers.end()));
|
||||
lnav_data.ld_active_files.fc_child_pollers.clear();
|
||||
|
||||
lnav::events::publish(
|
||||
lnav_data.ld_db.in(), new_files.fc_files, [](const auto& lf) {
|
||||
@ -393,12 +417,19 @@ rescan_files(bool req)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lnav_data.ld_active_files.fc_name_to_errors.count(pair.first)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lnav_data.ld_active_files.fc_synced_files.count(pair.first)
|
||||
== 0)
|
||||
{
|
||||
all_synced = false;
|
||||
}
|
||||
}
|
||||
if (!lnav_data.ld_active_files.fc_name_to_errors.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (!all_synced) {
|
||||
delay = 30ms;
|
||||
}
|
||||
|
@ -335,10 +335,25 @@ com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
std::string retval;
|
||||
|
||||
if (args.empty()) {
|
||||
args.emplace_back("move-time");
|
||||
args.emplace_back("move-args");
|
||||
} else if (args.size() > 1) {
|
||||
std::string all_args = remaining_args(cmdline, args);
|
||||
auto* tc = *lnav_data.ld_view_stack.top();
|
||||
nonstd::optional<vis_line_t> dst_vl;
|
||||
|
||||
if (startswith(all_args, "#")) {
|
||||
auto* ta = dynamic_cast<text_anchors*>(tc->get_sub_source());
|
||||
|
||||
if (ta == nullptr) {
|
||||
return ec.make_error("view does not support anchor links");
|
||||
}
|
||||
|
||||
dst_vl = ta->row_for_anchor(all_args);
|
||||
if (!dst_vl) {
|
||||
return ec.make_error("unable to find anchor: {}", all_args);
|
||||
}
|
||||
}
|
||||
|
||||
auto* ttt = dynamic_cast<text_time_translator*>(tc->get_sub_source());
|
||||
int line_number, consumed;
|
||||
date_time_scanner dts;
|
||||
@ -346,10 +361,12 @@ com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
struct timeval tv;
|
||||
struct exttm tm;
|
||||
float value;
|
||||
nonstd::optional<vis_line_t> dst_vl;
|
||||
auto parse_res = relative_time::from_str(all_args);
|
||||
|
||||
if (parse_res.isOk()) {
|
||||
if (dst_vl) {
|
||||
|
||||
}
|
||||
else if (parse_res.isOk()) {
|
||||
if (ttt == nullptr) {
|
||||
return ec.make_error(
|
||||
"relative time values only work in a time-indexed view");
|
||||
@ -2355,7 +2372,6 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
}
|
||||
|
||||
std::vector<std::string> word_exp;
|
||||
size_t colon_index;
|
||||
std::string pat;
|
||||
file_collection fc;
|
||||
|
||||
@ -2372,17 +2388,27 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
return ec.make_error("unable to parse arguments");
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, int>> files_to_front;
|
||||
std::vector<std::pair<std::string, file_location_t>> files_to_front;
|
||||
std::vector<std::string> closed_files;
|
||||
|
||||
for (auto fn : split_args) {
|
||||
int top = 0;
|
||||
file_location_t file_loc;
|
||||
|
||||
if (access(fn.c_str(), R_OK) != 0
|
||||
&& (colon_index = fn.rfind(':')) != std::string::npos)
|
||||
{
|
||||
if (sscanf(&fn.c_str()[colon_index + 1], "%d", &top) == 1) {
|
||||
fn = fn.substr(0, colon_index);
|
||||
if (access(fn.c_str(), R_OK) != 0) {
|
||||
auto colon_index = fn.rfind(':');
|
||||
auto hash_index = fn.rfind('#');
|
||||
if (colon_index != std::string::npos) {
|
||||
auto top_range = scn::string_view{
|
||||
&fn[colon_index + 1], &(*fn.cend())};
|
||||
auto scan_res = scn::scan_value<int>(top_range);
|
||||
|
||||
if (scan_res) {
|
||||
fn = fn.substr(0, colon_index);
|
||||
file_loc = vis_line_t(scan_res.value());
|
||||
}
|
||||
} else if (hash_index != std::string::npos) {
|
||||
file_loc = fn.substr(hash_index);
|
||||
fn = fn.substr(0, hash_index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2398,7 +2424,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
break;
|
||||
}
|
||||
|
||||
files_to_front.emplace_back(fn, top);
|
||||
files_to_front.emplace_back(fn, file_loc);
|
||||
retval = "";
|
||||
break;
|
||||
}
|
||||
@ -2418,7 +2444,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
ul->copy_fd());
|
||||
isc::to<curl_looper&, services::curl_streamer_t>().send(
|
||||
[ul](auto& clooper) { clooper.add_request(ul); });
|
||||
lnav_data.ld_files_to_front.emplace_back(fn, top);
|
||||
lnav_data.ld_files_to_front.emplace_back(fn, file_loc);
|
||||
retval = "info: opened URL";
|
||||
} else {
|
||||
retval = "";
|
||||
@ -2520,7 +2546,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
fn = abspath.in();
|
||||
fc.fc_file_names.emplace(fn, logfile_open_options());
|
||||
retval = "info: opened -- " + fn;
|
||||
files_to_front.emplace_back(fn, top);
|
||||
files_to_front.emplace_back(fn, file_loc);
|
||||
|
||||
closed_files.push_back(fn);
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
@ -2862,7 +2888,7 @@ com_comment(exec_context& ec,
|
||||
if (ec.ec_dry_run) {
|
||||
return Ok(std::string());
|
||||
}
|
||||
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
||||
auto* tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
return ec.make_error(
|
||||
@ -2871,12 +2897,15 @@ com_comment(exec_context& ec,
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
|
||||
args[1] = trim(remaining_args(cmdline, args));
|
||||
auto unquoted = auto_buffer::alloc(args[1].size() + 1);
|
||||
auto unquoted_len = unquote_content(unquoted.in(), args[1].c_str(), args[1].size(), 0);
|
||||
unquoted.resize(unquoted_len + 1);
|
||||
|
||||
tc->set_user_mark(&textview_curses::BM_META, tc->get_top(), true);
|
||||
|
||||
auto& line_meta = lss.get_bookmark_metadata(tc->get_top());
|
||||
|
||||
line_meta.bm_comment = args[1];
|
||||
line_meta.bm_comment = unquoted.in();
|
||||
lss.set_line_meta_changed();
|
||||
lss.text_filters_changed();
|
||||
tc->reload_data();
|
||||
@ -2902,7 +2931,11 @@ com_comment_prompt(exec_context& ec, const std::string& cmdline)
|
||||
auto line_meta_opt = lss.find_bookmark_metadata(tc->get_top());
|
||||
|
||||
if (line_meta_opt && !line_meta_opt.value()->bm_comment.empty()) {
|
||||
return trim(cmdline) + " " + trim(line_meta_opt.value()->bm_comment);
|
||||
auto trimmed_comment = trim(line_meta_opt.value()->bm_comment);
|
||||
auto buf = auto_buffer::alloc(trimmed_comment.size() + 16);
|
||||
quote_content(buf, trimmed_comment, 0);
|
||||
|
||||
return trim(cmdline) + " " + buf.to_string();
|
||||
}
|
||||
|
||||
return "";
|
||||
@ -4428,8 +4461,10 @@ com_quit(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
static void
|
||||
command_prompt(std::vector<std::string>& args)
|
||||
{
|
||||
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
||||
auto* tc = *lnav_data.ld_view_stack.top();
|
||||
auto* rlc = lnav_data.ld_rl_view;
|
||||
|
||||
rlc->clear_possibilities(ln_mode_t::COMMAND, "move-args");
|
||||
if (lnav_data.ld_views[LNV_LOG].get_inner_height() > 0) {
|
||||
static const char* MOVE_TIMES[]
|
||||
= {"here", "now", "today", "yesterday", nullptr};
|
||||
@ -4497,10 +4532,9 @@ command_prompt(std::vector<std::string>& args)
|
||||
|
||||
ldh.clear();
|
||||
|
||||
readline_curses* rlc = lnav_data.ld_rl_view;
|
||||
|
||||
rlc->clear_possibilities(ln_mode_t::COMMAND, "move-time");
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "move-time", MOVE_TIMES);
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "move-args", MOVE_TIMES);
|
||||
rlc->clear_possibilities(ln_mode_t::COMMAND, "line-time");
|
||||
{
|
||||
struct timeval tv = lf->get_time_offset();
|
||||
@ -4509,6 +4543,7 @@ command_prompt(std::vector<std::string>& args)
|
||||
sql_strftime(
|
||||
buffer, sizeof(buffer), ll->get_time(), ll->get_millis(), 'T');
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "line-time", buffer);
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "move-args", buffer);
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "move-time", buffer);
|
||||
sql_strftime(buffer,
|
||||
sizeof(buffer),
|
||||
@ -4516,6 +4551,7 @@ command_prompt(std::vector<std::string>& args)
|
||||
ll->get_millis() - (tv.tv_usec / 1000),
|
||||
'T');
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "line-time", buffer);
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "move-args", buffer);
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "move-time", buffer);
|
||||
}
|
||||
}
|
||||
@ -4537,6 +4573,11 @@ command_prompt(std::vector<std::string>& args)
|
||||
add_file_possibilities();
|
||||
add_recent_netlocs_possibilities();
|
||||
|
||||
auto *ta = dynamic_cast<text_anchors*>(tc->get_sub_source());
|
||||
if (ta != nullptr) {
|
||||
rlc->add_possibility(ln_mode_t::COMMAND, "move-args", ta->get_anchors());
|
||||
}
|
||||
|
||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||
add_filter_expr_possibilities(
|
||||
lnav_data.ld_rl_view, ln_mode_t::COMMAND, "filter-expr-syms");
|
||||
@ -4797,13 +4838,15 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
help_text(":goto")
|
||||
.with_summary("Go to the given location in the top view")
|
||||
.with_parameter(
|
||||
help_text("line#|N%|timestamp",
|
||||
"A line number, percent into the file, or a timestamp"))
|
||||
help_text("line#|N%|timestamp|#anchor",
|
||||
"A line number, percent into the file, timestamp, "
|
||||
"or an anchor in a text file"))
|
||||
.with_examples(
|
||||
{{"To go to line 22", "22"},
|
||||
{"To go to the line 75% of the way into the view", "75%"},
|
||||
{"To go to the first message on the first day of 2017",
|
||||
"2017-01-01"}})
|
||||
"2017-01-01"},
|
||||
{"To go to the Screenshots section", "#screenshots"}})
|
||||
.with_tags({"navigation"})},
|
||||
{"relative-goto",
|
||||
com_relative_goto,
|
||||
@ -5326,7 +5369,11 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
com_comment,
|
||||
|
||||
help_text(":comment")
|
||||
.with_summary("Attach a comment to the top log line")
|
||||
.with_summary(
|
||||
"Attach a comment to the top log line. The comment will be "
|
||||
"displayed right below the log message it is associated with. "
|
||||
"The comment can be formatted using markdown and you can add "
|
||||
"new-lines with '\\n'.")
|
||||
.with_parameter(help_text("text", "The comment text"))
|
||||
.with_example({"To add the comment 'This is where it all went "
|
||||
"wrong' to the top line",
|
||||
|
@ -620,42 +620,42 @@ static const struct json_path_container theme_styles_handlers = {
|
||||
.with_description("Styling for top-level headers")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
return &root->lt_style_header[0];
|
||||
return &root->lt_style_header[0].pp_value;
|
||||
})
|
||||
.with_children(style_config_handlers),
|
||||
yajlpp::property_handler("h2")
|
||||
.with_description("Styling for 2nd-level headers")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
return &root->lt_style_header[1];
|
||||
return &root->lt_style_header[1].pp_value;
|
||||
})
|
||||
.with_children(style_config_handlers),
|
||||
yajlpp::property_handler("h3")
|
||||
.with_description("Styling for 3rd-level headers")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
return &root->lt_style_header[2];
|
||||
return &root->lt_style_header[2].pp_value;
|
||||
})
|
||||
.with_children(style_config_handlers),
|
||||
yajlpp::property_handler("h4")
|
||||
.with_description("Styling for 4th-level headers")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
return &root->lt_style_header[3];
|
||||
return &root->lt_style_header[3].pp_value;
|
||||
})
|
||||
.with_children(style_config_handlers),
|
||||
yajlpp::property_handler("h5")
|
||||
.with_description("Styling for 5th-level headers")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
return &root->lt_style_header[4];
|
||||
return &root->lt_style_header[4].pp_value;
|
||||
})
|
||||
.with_children(style_config_handlers),
|
||||
yajlpp::property_handler("h6")
|
||||
.with_description("Styling for 6th-level headers")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
return &root->lt_style_header[5];
|
||||
return &root->lt_style_header[5].pp_value;
|
||||
})
|
||||
.with_children(style_config_handlers),
|
||||
yajlpp::property_handler("hr")
|
||||
@ -844,10 +844,14 @@ static const struct json_path_container theme_log_level_styles_handlers = {
|
||||
"warning|error|critical|fatal|invalid)")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
style_config& sc = root->lt_level_styles[string2level(
|
||||
auto& sc = root->lt_level_styles[string2level(
|
||||
ypc.ypc_extractor.get_substr_i("level").get())];
|
||||
|
||||
return ≻
|
||||
if (ypc.ypc_parse_context != nullptr && sc.pp_path.empty()) {
|
||||
sc.pp_path = ypc.ypc_parse_context->get_full_path();
|
||||
}
|
||||
|
||||
return &sc.pp_value;
|
||||
})
|
||||
.with_path_provider<lnav_theme>(
|
||||
[](struct lnav_theme* cfg, std::vector<std::string>& paths_out) {
|
||||
|
@ -1304,6 +1304,7 @@ load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
|
||||
|
||||
static void
|
||||
exec_sql_in_path(sqlite3* db,
|
||||
const std::map<std::string, scoped_value_t>& global_vars,
|
||||
const ghc::filesystem::path& path,
|
||||
std::vector<lnav::console::user_message>& errors)
|
||||
{
|
||||
@ -1321,7 +1322,7 @@ exec_sql_in_path(sqlite3* db,
|
||||
auto content = read_res.unwrap();
|
||||
|
||||
sql_execute_script(
|
||||
db, filename.c_str(), content.c_str(), errors);
|
||||
db, global_vars, filename.c_str(), content.c_str(), errors);
|
||||
} else {
|
||||
errors.emplace_back(
|
||||
lnav::console::user_message::error(
|
||||
@ -1335,11 +1336,12 @@ exec_sql_in_path(sqlite3* db,
|
||||
|
||||
void
|
||||
load_format_extra(sqlite3* db,
|
||||
const std::map<std::string, scoped_value_t>& global_vars,
|
||||
const std::vector<ghc::filesystem::path>& extra_paths,
|
||||
std::vector<lnav::console::user_message>& errors)
|
||||
{
|
||||
for (const auto& extra_path : extra_paths) {
|
||||
exec_sql_in_path(db, extra_path, errors);
|
||||
exec_sql_in_path(db, global_vars, extra_path, errors);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "base/intern_string.hh"
|
||||
#include "base/lnav.console.hh"
|
||||
#include "ghc/filesystem.hpp"
|
||||
#include "shlex.resolver.hh"
|
||||
|
||||
class log_vtab_manager;
|
||||
|
||||
@ -54,6 +55,7 @@ void load_format_vtabs(log_vtab_manager* vtab_manager,
|
||||
std::vector<lnav::console::user_message>& errors);
|
||||
|
||||
void load_format_extra(sqlite3* db,
|
||||
const std::map<std::string, scoped_value_t>& global_vars,
|
||||
const std::vector<ghc::filesystem::path>& extra_paths,
|
||||
std::vector<lnav::console::user_message>& errors);
|
||||
|
||||
|
@ -100,6 +100,11 @@ public:
|
||||
using iterator = std::vector<logline>::iterator;
|
||||
using const_iterator = std::vector<logline>::const_iterator;
|
||||
|
||||
struct metadata {
|
||||
text_format_t m_format;
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a logfile with the given arguments.
|
||||
*
|
||||
@ -360,6 +365,16 @@ public:
|
||||
return this->lf_bookmark_metadata;
|
||||
}
|
||||
|
||||
std::map<std::string, metadata>& get_embedded_metadata()
|
||||
{
|
||||
return this->lf_embedded_metadata;
|
||||
}
|
||||
|
||||
const std::map<std::string, metadata>& get_embedded_metadata() const
|
||||
{
|
||||
return this->lf_embedded_metadata;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Process a line from the file.
|
||||
@ -416,6 +431,7 @@ private:
|
||||
robin_hood::unordered_map<uint32_t, bookmark_metadata> lf_bookmark_metadata;
|
||||
|
||||
std::vector<std::shared_ptr<format_tag_def>> lf_applicable_taggers;
|
||||
std::map<std::string, metadata> lf_embedded_metadata;
|
||||
};
|
||||
|
||||
class logline_observer {
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "lnav.events.hh"
|
||||
#include "log_accel.hh"
|
||||
#include "logfile_sub_source.cfg.hh"
|
||||
#include "md2attr_line.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
#include "relative_time.hh"
|
||||
#include "sql_util.hh"
|
||||
@ -1908,9 +1909,19 @@ logfile_sub_source::meta_grepper::grep_value_for_line(vis_line_t line,
|
||||
if (!line_meta_opt) {
|
||||
value_out.clear();
|
||||
} else {
|
||||
bookmark_metadata& bm = *(line_meta_opt.value());
|
||||
auto& bm = *(line_meta_opt.value());
|
||||
|
||||
{
|
||||
md2attr_line mdal;
|
||||
|
||||
auto parse_res = md4cpp::parse(bm.bm_comment, mdal);
|
||||
if (parse_res.isOk()) {
|
||||
value_out.append(parse_res.unwrap().get_string());
|
||||
} else {
|
||||
value_out.append(bm.bm_comment);
|
||||
}
|
||||
}
|
||||
|
||||
value_out.append(bm.bm_comment);
|
||||
value_out.append("\x1c");
|
||||
for (const auto& tag : bm.bm_tags) {
|
||||
value_out.append(tag);
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "base/itertools.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "pcrepp/pcrepp.hh"
|
||||
#include "pugixml/pugixml.hpp"
|
||||
#include "readline_highlighters.hh"
|
||||
#include "view_curses.hh"
|
||||
|
||||
@ -145,8 +146,7 @@ md2attr_line::leave_block(const md4cpp::event_handler::block& bl)
|
||||
last_block.append("\n");
|
||||
}
|
||||
if (this->ml_list_stack.empty()
|
||||
&& !endswith(last_block.get_string(), "\n\n"))
|
||||
{
|
||||
&& !endswith(last_block.get_string(), "\n\n")) {
|
||||
last_block.append("\n");
|
||||
}
|
||||
}
|
||||
@ -208,8 +208,7 @@ md2attr_line::leave_block(const md4cpp::event_handler::block& bl)
|
||||
|
||||
for (auto line : block_text.split_lines()) {
|
||||
if (!cmd_block.empty()
|
||||
&& endswith(cmd_block.get_string(), "\\\n"))
|
||||
{
|
||||
&& endswith(cmd_block.get_string(), "\\\n")) {
|
||||
cmd_block.append(line).append("\n");
|
||||
continue;
|
||||
}
|
||||
@ -361,8 +360,7 @@ md2attr_line::leave_block(const md4cpp::event_handler::block& bl)
|
||||
}
|
||||
}
|
||||
for (size_t line_index = 0; line_index < max_cell_lines;
|
||||
line_index++)
|
||||
{
|
||||
line_index++) {
|
||||
size_t col = 0;
|
||||
for (const auto& cell : cells) {
|
||||
block_text.append(" ");
|
||||
@ -392,6 +390,12 @@ md2attr_line::leave_block(const md4cpp::event_handler::block& bl)
|
||||
} else if (bl.is<MD_BLOCK_TD_DETAIL*>()) {
|
||||
this->ml_tables.back().t_rows.back().r_columns.push_back(block_text);
|
||||
} else {
|
||||
if (bl.is<block_html>()) {
|
||||
if (startswith(block_text.get_string(), "<!--")) {
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
||||
text_wrap_settings tws = {0, this->ml_blocks.size() == 1 ? 70 : 10000};
|
||||
|
||||
if (!last_block.empty()) {
|
||||
@ -456,6 +460,15 @@ md2attr_line::leave_span(const md4cpp::event_handler::span& sp)
|
||||
lr,
|
||||
VC_STYLE.value(text_attrs{A_BOLD}),
|
||||
});
|
||||
} else if (sp.is<span_u>()) {
|
||||
line_range lr{
|
||||
static_cast<int>(this->ml_span_starts.back()),
|
||||
static_cast<int>(last_block.length()),
|
||||
};
|
||||
last_block.with_attr({
|
||||
lr,
|
||||
VC_STYLE.value(text_attrs{A_UNDERLINE}),
|
||||
});
|
||||
} else if (sp.is<MD_SPAN_A_DETAIL*>()) {
|
||||
auto* a_detail = sp.get<MD_SPAN_A_DETAIL*>();
|
||||
auto href_str = std::string(a_detail->href.text, a_detail->href.size);
|
||||
@ -475,6 +488,7 @@ Result<void, std::string>
|
||||
md2attr_line::text(MD_TEXTTYPE tt, const string_fragment& sf)
|
||||
{
|
||||
static const auto& entity_map = md4cpp::get_xml_entity_map();
|
||||
static const auto& vc = view_colors::singleton();
|
||||
|
||||
auto& last_block = this->ml_blocks.back();
|
||||
|
||||
@ -497,6 +511,45 @@ md2attr_line::text(MD_TEXTTYPE tt, const string_fragment& sf)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MD_TEXT_HTML: {
|
||||
last_block.append(sf);
|
||||
if (sf.startswith("<span ")) {
|
||||
this->ml_html_span_starts.push_back(last_block.length()
|
||||
- sf.length());
|
||||
} else if (sf == "</span>" && !this->ml_html_span_starts.empty()) {
|
||||
std::string html_span = last_block.get_string().substr(
|
||||
this->ml_html_span_starts.back());
|
||||
|
||||
pugi::xml_document doc;
|
||||
|
||||
auto load_res = doc.load_string(html_span.c_str());
|
||||
if (load_res) {
|
||||
auto span = doc.child("span");
|
||||
if (span) {
|
||||
auto styled_span = attr_line_t(span.text().get());
|
||||
|
||||
auto span_class = span.attribute("class");
|
||||
if (span_class) {
|
||||
auto cl_iter
|
||||
= vc.vc_class_to_role.find(span_class.value());
|
||||
|
||||
if (cl_iter == vc.vc_class_to_role.end()) {
|
||||
log_error("unknown span class: %s",
|
||||
span_class.value());
|
||||
} else {
|
||||
styled_span.with_attr_for_all(cl_iter->second);
|
||||
}
|
||||
}
|
||||
last_block.erase(this->ml_html_span_starts.back());
|
||||
last_block.append(styled_span);
|
||||
}
|
||||
} else {
|
||||
log_error("failed to parse: %s", load_res.description());
|
||||
}
|
||||
this->ml_html_span_starts.pop_back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
static const pcrepp REPL_RE(R"(-{2,3}|:[^:\s]*(?:::[^:\s]*)*:)");
|
||||
static const auto& emojis = md4cpp::get_emoji_map();
|
||||
@ -508,29 +561,36 @@ md2attr_line::text(MD_TEXTTYPE tt, const string_fragment& sf)
|
||||
|
||||
pcre_input pi(sf);
|
||||
pcre_context_static<30> pc;
|
||||
std::string span_text;
|
||||
|
||||
while (REPL_RE.match(pc, pi)) {
|
||||
auto prev = pi.get_up_to(pc.all());
|
||||
last_block.append(prev);
|
||||
span_text.append(prev.data(), prev.length());
|
||||
|
||||
auto matched = pi.get_string_fragment(pc.all());
|
||||
|
||||
if (matched == "--") {
|
||||
last_block.append("\u2013");
|
||||
span_text.append("\u2013");
|
||||
} else if (matched == "---") {
|
||||
last_block.append("\u2014");
|
||||
span_text.append("\u2014");
|
||||
} else if (matched.startswith(":")) {
|
||||
auto em_iter
|
||||
= emojis.em_shortname2emoji.find(matched.to_string());
|
||||
if (em_iter == emojis.em_shortname2emoji.end()) {
|
||||
last_block.append(matched);
|
||||
span_text.append(matched.data(), matched.length());
|
||||
} else {
|
||||
last_block.append(em_iter->second.get().e_value);
|
||||
span_text.append(em_iter->second.get().e_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->ml_blocks.back().append(sf.substr(pi.pi_offset));
|
||||
auto last_frag = sf.substr(pi.pi_offset);
|
||||
span_text.append(last_frag.data(), last_frag.length());
|
||||
|
||||
text_wrap_settings tws
|
||||
= {0, this->ml_blocks.size() == 1 ? 70 : 10000};
|
||||
|
||||
last_block.append(span_text, &tws);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ private:
|
||||
std::vector<list_block_t> ml_list_stack;
|
||||
std::vector<table_t> ml_tables;
|
||||
std::vector<size_t> ml_span_starts;
|
||||
std::vector<size_t> ml_html_span_starts;
|
||||
std::vector<attr_line_t> ml_footnotes;
|
||||
int32_t ml_code_depth{0};
|
||||
};
|
||||
|
432
src/md4cpp.cc
432
src/md4cpp.cc
@ -37,266 +37,254 @@
|
||||
|
||||
namespace md4cpp {
|
||||
|
||||
static const typed_json_path_container<xml_entity> xml_entity_handlers = {
|
||||
yajlpp::property_handler("characters").for_field(&xml_entity::xe_chars),
|
||||
};
|
||||
static const typed_json_path_container<xml_entity> xml_entity_handlers = {
|
||||
yajlpp::property_handler("characters").for_field(&xml_entity::xe_chars),
|
||||
};
|
||||
|
||||
static const typed_json_path_container<xml_entity_map> xml_entity_map_handlers
|
||||
= {
|
||||
yajlpp::pattern_property_handler("(?<var_name>\\&\\w+;?)")
|
||||
.with_synopsis("<name>")
|
||||
.with_path_provider<xml_entity_map>(
|
||||
[](struct xml_entity_map* xem,
|
||||
std::vector<std::string>& paths_out) {
|
||||
for (const auto& iter : xem->xem_entities) {
|
||||
paths_out.emplace_back(iter.first);
|
||||
}
|
||||
})
|
||||
.with_obj_provider<xml_entity, xml_entity_map>(
|
||||
[](const yajlpp_provider_context& ypc, xml_entity_map* xem) {
|
||||
auto entity_name = ypc.get_substr(0);
|
||||
return &xem->xem_entities[entity_name];
|
||||
})
|
||||
.with_children(xml_entity_handlers),
|
||||
};
|
||||
static const typed_json_path_container<xml_entity_map> xml_entity_map_handlers
|
||||
= {
|
||||
yajlpp::pattern_property_handler("(?<var_name>\\&\\w+;?)")
|
||||
.with_synopsis("<name>")
|
||||
.with_path_provider<xml_entity_map>(
|
||||
[](struct xml_entity_map *xem,
|
||||
std::vector<std::string> &paths_out) {
|
||||
for (const auto &iter: xem->xem_entities) {
|
||||
paths_out.emplace_back(iter.first);
|
||||
}
|
||||
})
|
||||
.with_obj_provider<xml_entity, xml_entity_map>(
|
||||
[](const yajlpp_provider_context &ypc, xml_entity_map *xem) {
|
||||
auto entity_name = ypc.get_substr(0);
|
||||
return &xem->xem_entities[entity_name];
|
||||
})
|
||||
.with_children(xml_entity_handlers),
|
||||
};
|
||||
|
||||
static const typed_json_path_container<emoji> emoji_handlers = {
|
||||
yajlpp::property_handler("emoji").for_field(&emoji::e_value),
|
||||
yajlpp::property_handler("shortname").for_field(&emoji::e_shortname),
|
||||
};
|
||||
static const typed_json_path_container<emoji> emoji_handlers = {
|
||||
yajlpp::property_handler("emoji").for_field(&emoji::e_value),
|
||||
yajlpp::property_handler("shortname").for_field(&emoji::e_shortname),
|
||||
};
|
||||
|
||||
static const typed_json_path_container<emoji_map> emoji_map_handlers = {
|
||||
yajlpp::property_handler("emojis#")
|
||||
.for_field(&emoji_map::em_emojis)
|
||||
.with_children(emoji_handlers),
|
||||
};
|
||||
static const typed_json_path_container<emoji_map> emoji_map_handlers = {
|
||||
yajlpp::property_handler("emojis#")
|
||||
.for_field(&emoji_map::em_emojis)
|
||||
.with_children(emoji_handlers),
|
||||
};
|
||||
|
||||
static xml_entity_map
|
||||
load_xml_entity_map()
|
||||
{
|
||||
static const intern_string_t name
|
||||
= intern_string::lookup(xml_entities_json.get_name());
|
||||
auto parse_res
|
||||
= xml_entity_map_handlers.parser_for(name).with_ignore_unused(true).of(
|
||||
xml_entities_json.to_string_fragment());
|
||||
static xml_entity_map
|
||||
load_xml_entity_map() {
|
||||
static const intern_string_t name
|
||||
= intern_string::lookup(xml_entities_json.get_name());
|
||||
auto parse_res
|
||||
= xml_entity_map_handlers.parser_for(name).with_ignore_unused(true).of(
|
||||
xml_entities_json.to_string_fragment());
|
||||
|
||||
assert(parse_res.isOk());
|
||||
assert(parse_res.isOk());
|
||||
|
||||
return parse_res.unwrap();
|
||||
}
|
||||
|
||||
const xml_entity_map&
|
||||
get_xml_entity_map()
|
||||
{
|
||||
static const auto retval = load_xml_entity_map();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static emoji_map
|
||||
load_emoji_map()
|
||||
{
|
||||
static const intern_string_t name
|
||||
= intern_string::lookup(emojis_json.get_name());
|
||||
auto parse_res
|
||||
= emoji_map_handlers.parser_for(name).with_ignore_unused(true).of(
|
||||
emojis_json.to_string_fragment());
|
||||
|
||||
assert(parse_res.isOk());
|
||||
|
||||
auto retval = parse_res.unwrap();
|
||||
for (auto& em : retval.em_emojis) {
|
||||
retval.em_shortname2emoji.emplace(em.e_shortname, em);
|
||||
return parse_res.unwrap();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
const xml_entity_map &
|
||||
get_xml_entity_map() {
|
||||
static const auto retval = load_xml_entity_map();
|
||||
|
||||
const emoji_map&
|
||||
get_emoji_map()
|
||||
{
|
||||
static const auto retval = load_emoji_map();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct parse_userdata {
|
||||
event_handler& pu_handler;
|
||||
std::string pu_error_msg;
|
||||
};
|
||||
|
||||
static event_handler::block
|
||||
build_block(MD_BLOCKTYPE type, void* detail)
|
||||
{
|
||||
switch (type) {
|
||||
case MD_BLOCK_DOC:
|
||||
return event_handler::block_doc{};
|
||||
case MD_BLOCK_QUOTE:
|
||||
return event_handler::block_quote{};
|
||||
case MD_BLOCK_UL:
|
||||
return static_cast<MD_BLOCK_UL_DETAIL*>(detail);
|
||||
case MD_BLOCK_OL:
|
||||
return static_cast<MD_BLOCK_OL_DETAIL*>(detail);
|
||||
case MD_BLOCK_LI:
|
||||
return static_cast<MD_BLOCK_LI_DETAIL*>(detail);
|
||||
case MD_BLOCK_HR:
|
||||
return event_handler::block_hr{};
|
||||
case MD_BLOCK_H:
|
||||
return static_cast<MD_BLOCK_H_DETAIL*>(detail);
|
||||
case MD_BLOCK_CODE:
|
||||
return static_cast<MD_BLOCK_CODE_DETAIL*>(detail);
|
||||
case MD_BLOCK_HTML:
|
||||
return event_handler::block_html{};
|
||||
case MD_BLOCK_P:
|
||||
return event_handler::block_p{};
|
||||
case MD_BLOCK_TABLE:
|
||||
return static_cast<MD_BLOCK_TABLE_DETAIL*>(detail);
|
||||
case MD_BLOCK_THEAD:
|
||||
return event_handler::block_thead{};
|
||||
case MD_BLOCK_TBODY:
|
||||
return event_handler::block_tbody{};
|
||||
case MD_BLOCK_TR:
|
||||
return event_handler::block_tr{};
|
||||
case MD_BLOCK_TH:
|
||||
return event_handler::block_th{};
|
||||
case MD_BLOCK_TD:
|
||||
return static_cast<MD_BLOCK_TD_DETAIL*>(detail);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
static emoji_map
|
||||
load_emoji_map() {
|
||||
static const intern_string_t name
|
||||
= intern_string::lookup(emojis_json.get_name());
|
||||
auto parse_res
|
||||
= emoji_map_handlers.parser_for(name).with_ignore_unused(true).of(
|
||||
emojis_json.to_string_fragment());
|
||||
|
||||
static event_handler::span
|
||||
build_span(MD_SPANTYPE type, void* detail)
|
||||
{
|
||||
switch (type) {
|
||||
case MD_SPAN_EM:
|
||||
return event_handler::span_em{};
|
||||
case MD_SPAN_STRONG:
|
||||
return event_handler::span_strong{};
|
||||
case MD_SPAN_A:
|
||||
return static_cast<MD_SPAN_A_DETAIL*>(detail);
|
||||
case MD_SPAN_IMG:
|
||||
return static_cast<MD_SPAN_IMG_DETAIL*>(detail);
|
||||
case MD_SPAN_CODE:
|
||||
return event_handler::span_code{};
|
||||
case MD_SPAN_DEL:
|
||||
return event_handler::span_del{};
|
||||
case MD_SPAN_U:
|
||||
return event_handler::span_u{};
|
||||
default:
|
||||
break;
|
||||
assert(parse_res.isOk());
|
||||
|
||||
auto retval = parse_res.unwrap();
|
||||
for (auto &em: retval.em_emojis) {
|
||||
retval.em_shortname2emoji.emplace(em.e_shortname, em);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
const emoji_map &
|
||||
get_emoji_map() {
|
||||
static const auto retval = load_emoji_map();
|
||||
|
||||
static int
|
||||
md4cpp_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
auto* pu = static_cast<parse_userdata*>(userdata);
|
||||
|
||||
auto enter_res = pu->pu_handler.enter_block(build_block(type, detail));
|
||||
if (enter_res.isErr()) {
|
||||
pu->pu_error_msg = enter_res.unwrapErr();
|
||||
return 1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
struct parse_userdata {
|
||||
event_handler &pu_handler;
|
||||
std::string pu_error_msg;
|
||||
};
|
||||
|
||||
static int
|
||||
md4cpp_leave_block(MD_BLOCKTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
auto* pu = static_cast<parse_userdata*>(userdata);
|
||||
static event_handler::block
|
||||
build_block(MD_BLOCKTYPE type, void *detail) {
|
||||
switch (type) {
|
||||
case MD_BLOCK_DOC:
|
||||
return event_handler::block_doc{};
|
||||
case MD_BLOCK_QUOTE:
|
||||
return event_handler::block_quote{};
|
||||
case MD_BLOCK_UL:
|
||||
return static_cast<MD_BLOCK_UL_DETAIL *>(detail);
|
||||
case MD_BLOCK_OL:
|
||||
return static_cast<MD_BLOCK_OL_DETAIL *>(detail);
|
||||
case MD_BLOCK_LI:
|
||||
return static_cast<MD_BLOCK_LI_DETAIL *>(detail);
|
||||
case MD_BLOCK_HR:
|
||||
return event_handler::block_hr{};
|
||||
case MD_BLOCK_H:
|
||||
return static_cast<MD_BLOCK_H_DETAIL *>(detail);
|
||||
case MD_BLOCK_CODE:
|
||||
return static_cast<MD_BLOCK_CODE_DETAIL *>(detail);
|
||||
case MD_BLOCK_HTML:
|
||||
return event_handler::block_html{};
|
||||
case MD_BLOCK_P:
|
||||
return event_handler::block_p{};
|
||||
case MD_BLOCK_TABLE:
|
||||
return static_cast<MD_BLOCK_TABLE_DETAIL *>(detail);
|
||||
case MD_BLOCK_THEAD:
|
||||
return event_handler::block_thead{};
|
||||
case MD_BLOCK_TBODY:
|
||||
return event_handler::block_tbody{};
|
||||
case MD_BLOCK_TR:
|
||||
return event_handler::block_tr{};
|
||||
case MD_BLOCK_TH:
|
||||
return event_handler::block_th{};
|
||||
case MD_BLOCK_TD:
|
||||
return static_cast<MD_BLOCK_TD_DETAIL *>(detail);
|
||||
}
|
||||
|
||||
auto leave_res = pu->pu_handler.leave_block(build_block(type, detail));
|
||||
if (leave_res.isErr()) {
|
||||
pu->pu_error_msg = leave_res.unwrapErr();
|
||||
return 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static event_handler::span
|
||||
build_span(MD_SPANTYPE type, void *detail) {
|
||||
switch (type) {
|
||||
case MD_SPAN_EM:
|
||||
return event_handler::span_em{};
|
||||
case MD_SPAN_STRONG:
|
||||
return event_handler::span_strong{};
|
||||
case MD_SPAN_A:
|
||||
return static_cast<MD_SPAN_A_DETAIL *>(detail);
|
||||
case MD_SPAN_IMG:
|
||||
return static_cast<MD_SPAN_IMG_DETAIL *>(detail);
|
||||
case MD_SPAN_CODE:
|
||||
return event_handler::span_code{};
|
||||
case MD_SPAN_DEL:
|
||||
return event_handler::span_del{};
|
||||
case MD_SPAN_U:
|
||||
return event_handler::span_u{};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
static int
|
||||
md4cpp_enter_span(MD_SPANTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
auto* pu = static_cast<parse_userdata*>(userdata);
|
||||
|
||||
auto enter_res = pu->pu_handler.enter_span(build_span(type, detail));
|
||||
if (enter_res.isErr()) {
|
||||
pu->pu_error_msg = enter_res.unwrapErr();
|
||||
return 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int
|
||||
md4cpp_enter_block(MD_BLOCKTYPE type, void *detail, void *userdata) {
|
||||
auto *pu = static_cast<parse_userdata *>(userdata);
|
||||
|
||||
static int
|
||||
md4cpp_leave_span(MD_SPANTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
auto* pu = static_cast<parse_userdata*>(userdata);
|
||||
auto enter_res = pu->pu_handler.enter_block(build_block(type, detail));
|
||||
if (enter_res.isErr()) {
|
||||
pu->pu_error_msg = enter_res.unwrapErr();
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto leave_res = pu->pu_handler.leave_span(build_span(type, detail));
|
||||
if (leave_res.isErr()) {
|
||||
pu->pu_error_msg = leave_res.unwrapErr();
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int
|
||||
md4cpp_leave_block(MD_BLOCKTYPE type, void *detail, void *userdata) {
|
||||
auto *pu = static_cast<parse_userdata *>(userdata);
|
||||
|
||||
static int
|
||||
md4cpp_text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata)
|
||||
{
|
||||
auto* pu = static_cast<parse_userdata*>(userdata);
|
||||
auto leave_res = pu->pu_handler.text(type, string_fragment(text, 0, size));
|
||||
if (leave_res.isErr()) {
|
||||
pu->pu_error_msg = leave_res.unwrapErr();
|
||||
return 1;
|
||||
auto leave_res = pu->pu_handler.leave_block(build_block(type, detail));
|
||||
if (leave_res.isErr()) {
|
||||
pu->pu_error_msg = leave_res.unwrapErr();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int
|
||||
md4cpp_enter_span(MD_SPANTYPE type, void *detail, void *userdata) {
|
||||
auto *pu = static_cast<parse_userdata *>(userdata);
|
||||
|
||||
namespace details {
|
||||
Result<void, std::string>
|
||||
parse(const string_fragment& sf, event_handler& eh)
|
||||
{
|
||||
const char* utf8_errmsg = nullptr;
|
||||
int utf8_faulty_bytes = 0;
|
||||
auto enter_res = pu->pu_handler.enter_span(build_span(type, detail));
|
||||
if (enter_res.isErr()) {
|
||||
pu->pu_error_msg = enter_res.unwrapErr();
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto utf8_erroff = is_utf8((unsigned char*) sf.data(),
|
||||
sf.length(),
|
||||
&utf8_errmsg,
|
||||
&utf8_faulty_bytes);
|
||||
if (utf8_errmsg != nullptr) {
|
||||
return Err(
|
||||
fmt::format(FMT_STRING("file has invalid UTF-8 at offset {}: {}"),
|
||||
utf8_erroff,
|
||||
utf8_errmsg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
MD_PARSER parser = {0};
|
||||
auto pu = parse_userdata{eh};
|
||||
static int
|
||||
md4cpp_leave_span(MD_SPANTYPE type, void *detail, void *userdata) {
|
||||
auto *pu = static_cast<parse_userdata *>(userdata);
|
||||
|
||||
parser.abi_version = 0;
|
||||
parser.flags = MD_DIALECT_GITHUB;
|
||||
parser.enter_block = md4cpp_enter_block;
|
||||
parser.leave_block = md4cpp_leave_block;
|
||||
parser.enter_span = md4cpp_enter_span;
|
||||
parser.leave_span = md4cpp_leave_span;
|
||||
parser.text = md4cpp_text;
|
||||
auto leave_res = pu->pu_handler.leave_span(build_span(type, detail));
|
||||
if (leave_res.isErr()) {
|
||||
pu->pu_error_msg = leave_res.unwrapErr();
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto rc = md_parse(sf.data(), sf.length(), &parser, &pu);
|
||||
|
||||
if (rc == 0) {
|
||||
return Ok();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Err(pu.pu_error_msg);
|
||||
}
|
||||
} // namespace details
|
||||
static int
|
||||
md4cpp_text(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *userdata) {
|
||||
auto *pu = static_cast<parse_userdata *>(userdata);
|
||||
auto text_res = pu->pu_handler.text(type, string_fragment(text, 0, size));
|
||||
if (text_res.isErr()) {
|
||||
pu->pu_error_msg = text_res.unwrapErr();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace details {
|
||||
Result<void, std::string>
|
||||
parse(const string_fragment &sf, event_handler &eh) {
|
||||
const char *utf8_errmsg = nullptr;
|
||||
int utf8_faulty_bytes = 0;
|
||||
|
||||
auto utf8_erroff = is_utf8((unsigned char *) sf.data(),
|
||||
sf.length(),
|
||||
&utf8_errmsg,
|
||||
&utf8_faulty_bytes);
|
||||
if (utf8_errmsg != nullptr) {
|
||||
return Err(
|
||||
fmt::format(FMT_STRING("file has invalid UTF-8 at offset {}: {}"),
|
||||
utf8_erroff,
|
||||
utf8_errmsg));
|
||||
}
|
||||
|
||||
MD_PARSER parser = {0};
|
||||
auto pu = parse_userdata{eh};
|
||||
|
||||
parser.abi_version = 0;
|
||||
parser.flags = MD_DIALECT_GITHUB | MD_FLAG_UNDERLINE;
|
||||
parser.enter_block = md4cpp_enter_block;
|
||||
parser.leave_block = md4cpp_leave_block;
|
||||
parser.enter_span = md4cpp_enter_span;
|
||||
parser.leave_span = md4cpp_leave_span;
|
||||
parser.text = md4cpp_text;
|
||||
|
||||
auto rc = md_parse(sf.data(), sf.length(), &parser, &pu);
|
||||
|
||||
if (rc == 0) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
return Err(pu.pu_error_msg);
|
||||
}
|
||||
} // namespace details
|
||||
|
||||
} // namespace md4cpp
|
||||
|
@ -303,3 +303,71 @@ plain_text_source::text_crumbs_for_line(int line,
|
||||
: breadcrumb::crumb::expected_input_t::index_or_exact;
|
||||
}
|
||||
}
|
||||
|
||||
nonstd::optional<vis_line_t>
|
||||
plain_text_source::row_for_anchor(const std::string& id)
|
||||
{
|
||||
nonstd::optional<vis_line_t> retval;
|
||||
|
||||
if (this->tds_doc_sections.m_sections_root == nullptr) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
lnav::document::hier_node::depth_first(
|
||||
this->tds_doc_sections.m_sections_root.get(),
|
||||
[this, &id, &retval](const lnav::document::hier_node* node) {
|
||||
for (const auto& child_pair : node->hn_named_children) {
|
||||
auto child_anchor
|
||||
= text_anchors::to_anchor_string(child_pair.first);
|
||||
|
||||
if (child_anchor == id) {
|
||||
retval = this->line_for_offset(child_pair.second->hn_start);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string>
|
||||
plain_text_source::get_anchors()
|
||||
{
|
||||
std::unordered_set<std::string> retval;
|
||||
|
||||
lnav::document::hier_node::depth_first(
|
||||
this->tds_doc_sections.m_sections_root.get(),
|
||||
[&retval](const lnav::document::hier_node* node) {
|
||||
for (const auto& child_pair : node->hn_named_children) {
|
||||
retval.emplace(
|
||||
text_anchors::to_anchor_string(child_pair.first));
|
||||
}
|
||||
});
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
nonstd::optional<std::string>
|
||||
plain_text_source::anchor_for_row(vis_line_t vl)
|
||||
{
|
||||
nonstd::optional<std::string> retval;
|
||||
|
||||
if (vl > this->tds_lines.size()
|
||||
|| this->tds_doc_sections.m_sections_root == nullptr)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
|
||||
const auto& tl = this->tds_lines[vl];
|
||||
|
||||
this->tds_doc_sections.m_sections_tree.visit_overlapping(
|
||||
tl.tl_offset, [&retval](const lnav::document::section_interval_t& iv) {
|
||||
retval = iv.value.match(
|
||||
[](const std::string& str) {
|
||||
return nonstd::make_optional(
|
||||
text_anchors::to_anchor_string(str));
|
||||
},
|
||||
[](size_t) { return nonstd::nullopt; });
|
||||
});
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -40,7 +40,8 @@
|
||||
|
||||
class plain_text_source
|
||||
: public text_sub_source
|
||||
, public vis_location_history {
|
||||
, public vis_location_history
|
||||
, public text_anchors {
|
||||
public:
|
||||
struct text_line {
|
||||
text_line(file_off_t off, attr_line_t value)
|
||||
@ -82,10 +83,7 @@ public:
|
||||
|
||||
size_t text_line_count() override { return this->tds_lines.size(); }
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return this->tds_lines.empty();
|
||||
}
|
||||
bool empty() const { return this->tds_lines.empty(); }
|
||||
|
||||
size_t text_line_width(textview_curses& curses) override;
|
||||
|
||||
@ -120,6 +118,10 @@ public:
|
||||
void text_crumbs_for_line(int line,
|
||||
std::vector<breadcrumb::crumb>& crumbs) override;
|
||||
|
||||
nonstd::optional<vis_line_t> row_for_anchor(const std::string& id) override;
|
||||
nonstd::optional<std::string> anchor_for_row(vis_line_t vl) override;
|
||||
std::unordered_set<std::string> get_anchors() override;
|
||||
|
||||
protected:
|
||||
size_t compute_longest_line();
|
||||
|
||||
|
@ -364,7 +364,8 @@ rl_change(readline_curses* rc)
|
||||
}
|
||||
|
||||
if (cmd.c_prompt != nullptr && generation == 0
|
||||
&& trim(line) == args[0]) {
|
||||
&& trim(line) == args[0])
|
||||
{
|
||||
const auto new_prompt
|
||||
= cmd.c_prompt(lnav_data.ld_exec_context, line);
|
||||
|
||||
@ -386,7 +387,8 @@ rl_change(readline_curses* rc)
|
||||
auto iter = scripts.as_scripts.find(script_name);
|
||||
|
||||
if (iter == scripts.as_scripts.end()
|
||||
|| iter->second[0].sm_description.empty()) {
|
||||
|| iter->second[0].sm_description.empty())
|
||||
{
|
||||
lnav_data.ld_bottom_source.set_prompt(
|
||||
"Enter a script to execute: " ABORT_MSG);
|
||||
} else {
|
||||
@ -779,7 +781,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
||||
.with_fd(std::move(fd_copy))
|
||||
.with_include_in_session(false)
|
||||
.with_detect_format(false);
|
||||
lnav_data.ld_files_to_front.emplace_back(desc, 0);
|
||||
lnav_data.ld_files_to_front.emplace_back(desc, 0_vl);
|
||||
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/intern_string.hh"
|
||||
#include "fmt/format.h"
|
||||
#include "mapbox/variant.hpp"
|
||||
|
||||
@ -85,10 +86,7 @@ public:
|
||||
return this->end();
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return this->sr_stack.back()->end();
|
||||
}
|
||||
const_iterator end() const { return this->sr_stack.back()->end(); }
|
||||
|
||||
std::vector<const std::map<std::string, scoped_value_t>*> sr_stack;
|
||||
};
|
||||
|
205
src/sql_util.cc
205
src/sql_util.cc
@ -50,6 +50,7 @@
|
||||
#include "pcrepp/pcrepp.hh"
|
||||
#include "readline_context.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
#include "shlex.resolver.hh"
|
||||
#include "sql_help.hh"
|
||||
#include "sqlite-extension-func.hh"
|
||||
|
||||
@ -727,11 +728,109 @@ annotate_sql_with_error(sqlite3* db, const char* sql, const char* tail)
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
sql_execute_script(sqlite3* db,
|
||||
const std::map<std::string, scoped_value_t>& global_vars,
|
||||
const char* src_name,
|
||||
sqlite3_stmt* stmt,
|
||||
std::vector<lnav::console::user_message>& errors)
|
||||
{
|
||||
std::map<std::string, scoped_value_t> lvars;
|
||||
bool done = false;
|
||||
int param_count;
|
||||
|
||||
sqlite3_clear_bindings(stmt);
|
||||
|
||||
param_count = sqlite3_bind_parameter_count(stmt);
|
||||
for (int lpc = 0; lpc < param_count; lpc++) {
|
||||
const char* name;
|
||||
|
||||
name = sqlite3_bind_parameter_name(stmt, lpc + 1);
|
||||
if (name[0] == '$') {
|
||||
const char* env_value;
|
||||
auto iter = lvars.find(&name[1]);
|
||||
if (iter != lvars.end()) {
|
||||
mapbox::util::apply_visitor(
|
||||
sqlitepp::bind_visitor(stmt, lpc + 1), iter->second);
|
||||
} else {
|
||||
auto giter = global_vars.find(&name[1]);
|
||||
if (giter != global_vars.end()) {
|
||||
mapbox::util::apply_visitor(
|
||||
sqlitepp::bind_visitor(stmt, lpc + 1), giter->second);
|
||||
} else if ((env_value = getenv(&name[1])) != nullptr) {
|
||||
sqlite3_bind_text(
|
||||
stmt, lpc + 1, env_value, -1, SQLITE_TRANSIENT);
|
||||
} else {
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
}
|
||||
}
|
||||
while (!done) {
|
||||
int retcode = sqlite3_step(stmt);
|
||||
switch (retcode) {
|
||||
case SQLITE_OK:
|
||||
case SQLITE_DONE:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case SQLITE_ROW: {
|
||||
int ncols = sqlite3_column_count(stmt);
|
||||
|
||||
for (int lpc = 0; lpc < ncols; lpc++) {
|
||||
const char* name = sqlite3_column_name(stmt, lpc);
|
||||
auto* raw_value = sqlite3_column_value(stmt, lpc);
|
||||
auto value_type = sqlite3_value_type(raw_value);
|
||||
scoped_value_t value;
|
||||
|
||||
switch (value_type) {
|
||||
case SQLITE_INTEGER:
|
||||
value = (int64_t) sqlite3_value_int64(raw_value);
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
value = sqlite3_value_double(raw_value);
|
||||
break;
|
||||
case SQLITE_NULL:
|
||||
value = null_value_t{};
|
||||
break;
|
||||
default:
|
||||
value = string_fragment::from_bytes(
|
||||
sqlite3_value_text(raw_value),
|
||||
sqlite3_value_bytes(raw_value));
|
||||
break;
|
||||
}
|
||||
lvars[name] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const auto* sql_str = sqlite3_sql(stmt);
|
||||
auto sql_content
|
||||
= annotate_sql_with_error(db, sql_str, nullptr);
|
||||
|
||||
errors.emplace_back(
|
||||
lnav::console::user_message::error(
|
||||
"failed to execute SQL statement")
|
||||
.with_reason(sqlite3_errmsg_to_attr_line(db))
|
||||
.with_snippet(lnav::console::snippet::from(
|
||||
intern_string::lookup(src_name), sql_content)));
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
}
|
||||
|
||||
static void
|
||||
sql_compile_script(sqlite3* db,
|
||||
const std::map<std::string, scoped_value_t>& global_vars,
|
||||
const char* src_name,
|
||||
const char* script_orig,
|
||||
std::vector<sqlite3_stmt*>& stmts,
|
||||
std::vector<lnav::console::user_message>& errors)
|
||||
{
|
||||
const char* script = script_orig;
|
||||
@ -766,115 +865,27 @@ sql_compile_script(sqlite3* db,
|
||||
intern_string::lookup(src_name), sql_content)
|
||||
.with_line(line_number)));
|
||||
break;
|
||||
} else if (script == tail) {
|
||||
}
|
||||
if (script == tail) {
|
||||
break;
|
||||
} else if (stmt == nullptr) {
|
||||
}
|
||||
if (stmt == nullptr) {
|
||||
} else {
|
||||
stmts.push_back(stmt.release());
|
||||
sql_execute_script(db, global_vars, src_name, stmt.in(), errors);
|
||||
}
|
||||
|
||||
script = tail;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sql_execute_script(sqlite3* db,
|
||||
const char* src_name,
|
||||
const std::vector<sqlite3_stmt*>& stmts,
|
||||
std::vector<lnav::console::user_message>& errors)
|
||||
{
|
||||
std::map<std::string, std::string> lvars;
|
||||
|
||||
for (sqlite3_stmt* stmt : stmts) {
|
||||
bool done = false;
|
||||
int param_count;
|
||||
|
||||
sqlite3_clear_bindings(stmt);
|
||||
|
||||
param_count = sqlite3_bind_parameter_count(stmt);
|
||||
for (int lpc = 0; lpc < param_count; lpc++) {
|
||||
const char* name;
|
||||
|
||||
name = sqlite3_bind_parameter_name(stmt, lpc + 1);
|
||||
if (name[0] == '$') {
|
||||
std::map<std::string, std::string>::iterator iter;
|
||||
const char* env_value;
|
||||
|
||||
if ((iter = lvars.find(&name[1])) != lvars.end()) {
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
iter->second.c_str(),
|
||||
-1,
|
||||
SQLITE_TRANSIENT);
|
||||
} else if ((env_value = getenv(&name[1])) != nullptr) {
|
||||
sqlite3_bind_text(
|
||||
stmt, lpc + 1, env_value, -1, SQLITE_TRANSIENT);
|
||||
} else {
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
}
|
||||
} else {
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
}
|
||||
}
|
||||
while (!done) {
|
||||
int retcode = sqlite3_step(stmt);
|
||||
switch (retcode) {
|
||||
case SQLITE_OK:
|
||||
case SQLITE_DONE:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case SQLITE_ROW: {
|
||||
int ncols = sqlite3_column_count(stmt);
|
||||
|
||||
for (int lpc = 0; lpc < ncols; lpc++) {
|
||||
const char* name = sqlite3_column_name(stmt, lpc);
|
||||
const char* value
|
||||
= (const char*) sqlite3_column_text(stmt, lpc);
|
||||
|
||||
lvars[name] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const auto* sql_str = sqlite3_sql(stmt);
|
||||
auto sql_content
|
||||
= annotate_sql_with_error(db, sql_str, nullptr);
|
||||
|
||||
errors.emplace_back(
|
||||
lnav::console::user_message::error(
|
||||
"failed to execute SQL statement")
|
||||
.with_reason(sqlite3_errmsg_to_attr_line(db))
|
||||
.with_snippet(lnav::console::snippet::from(
|
||||
intern_string::lookup(src_name), sql_content)));
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sql_execute_script(sqlite3* db,
|
||||
const std::map<std::string, scoped_value_t>& global_vars,
|
||||
const char* src_name,
|
||||
const char* script,
|
||||
std::vector<lnav::console::user_message>& errors)
|
||||
{
|
||||
std::vector<sqlite3_stmt*> stmts;
|
||||
auto init_error_count = errors.size();
|
||||
|
||||
sql_compile_script(db, src_name, script, stmts, errors);
|
||||
if (errors.size() == init_error_count) {
|
||||
sql_execute_script(db, src_name, stmts, errors);
|
||||
}
|
||||
|
||||
for (sqlite3_stmt* stmt : stmts) {
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
sql_compile_script(db, global_vars, src_name, script, errors);
|
||||
}
|
||||
|
||||
static struct {
|
||||
|
@ -105,16 +105,12 @@ char* sql_quote_ident(const char* ident);
|
||||
|
||||
std::string sql_safe_ident(const string_fragment& ident);
|
||||
|
||||
void sql_compile_script(sqlite3* db,
|
||||
const char* src_name,
|
||||
const char* script,
|
||||
std::vector<sqlite3_stmt*>& stmts,
|
||||
std::vector<lnav::console::user_message>& errors);
|
||||
|
||||
void sql_execute_script(sqlite3* db,
|
||||
const char* src_name,
|
||||
const char* script,
|
||||
std::vector<lnav::console::user_message>& errors);
|
||||
void sql_execute_script(
|
||||
sqlite3* db,
|
||||
const std::map<std::string, scoped_value_t>& global_vars,
|
||||
const char* src_name,
|
||||
const char* script,
|
||||
std::vector<lnav::console::user_message>& errors);
|
||||
|
||||
int guess_type_from_pcre(const std::string& pattern, std::string& collator);
|
||||
|
||||
|
@ -46,16 +46,18 @@ int sqlite3_series_init(sqlite3* db,
|
||||
const sqlite3_api_routines* pApi);
|
||||
}
|
||||
|
||||
sqlite_registration_func_t sqlite_registration_funcs[]
|
||||
= {common_extension_functions,
|
||||
state_extension_functions,
|
||||
string_extension_functions,
|
||||
network_extension_functions,
|
||||
fs_extension_functions,
|
||||
json_extension_functions,
|
||||
time_extension_functions,
|
||||
sqlite_registration_func_t sqlite_registration_funcs[] = {
|
||||
common_extension_functions,
|
||||
state_extension_functions,
|
||||
string_extension_functions,
|
||||
network_extension_functions,
|
||||
fs_extension_functions,
|
||||
json_extension_functions,
|
||||
yaml_extension_functions,
|
||||
time_extension_functions,
|
||||
|
||||
nullptr};
|
||||
nullptr,
|
||||
};
|
||||
|
||||
int
|
||||
register_sqlite_funcs(sqlite3* db, sqlite_registration_func_t* reg_funcs)
|
||||
|
@ -88,11 +88,15 @@ int json_extension_functions(struct FuncDef** basic_funcs,
|
||||
int time_extension_functions(struct FuncDef** basic_funcs,
|
||||
struct FuncDefAgg** agg_funcs);
|
||||
|
||||
int yaml_extension_functions(struct FuncDef** basic_funcs,
|
||||
struct FuncDefAgg** agg_funcs);
|
||||
|
||||
extern sqlite_registration_func_t sqlite_registration_funcs[];
|
||||
|
||||
int register_sqlite_funcs(sqlite3* db, sqlite_registration_func_t* reg_funcs);
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
int sqlite3_db_dump(
|
||||
sqlite3* db, /* The database connection */
|
||||
const char* zSchema, /* Which schema to dump. Usually "main". */
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
#include "base/auto_mem.hh"
|
||||
#include "optional.hpp"
|
||||
#include "shlex.resolver.hh"
|
||||
|
||||
/* XXX figure out how to do this with the template */
|
||||
void sqlite_close_wrapper(void* mem);
|
||||
@ -60,6 +61,48 @@ quote(const nonstd::optional<std::string>& str)
|
||||
|
||||
extern const char* ERROR_PREFIX;
|
||||
|
||||
struct bind_visitor {
|
||||
bind_visitor(sqlite3_stmt* stmt, int index) : bv_stmt(stmt), bv_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const std::string& str) const
|
||||
{
|
||||
sqlite3_bind_text(this->bv_stmt,
|
||||
this->bv_index,
|
||||
str.c_str(),
|
||||
str.size(),
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
void operator()(const string_fragment& str) const
|
||||
{
|
||||
sqlite3_bind_text(this->bv_stmt,
|
||||
this->bv_index,
|
||||
str.data(),
|
||||
str.length(),
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
void operator()(null_value_t) const
|
||||
{
|
||||
sqlite3_bind_null(this->bv_stmt, this->bv_index);
|
||||
}
|
||||
|
||||
void operator()(int64_t value) const
|
||||
{
|
||||
sqlite3_bind_int64(this->bv_stmt, this->bv_index, value);
|
||||
}
|
||||
|
||||
void operator()(double value) const
|
||||
{
|
||||
sqlite3_bind_double(this->bv_stmt, this->bv_index, value);
|
||||
}
|
||||
|
||||
sqlite3_stmt* bv_stmt;
|
||||
int bv_index;
|
||||
};
|
||||
|
||||
} // namespace sqlitepp
|
||||
|
||||
#endif
|
||||
|
333
src/static_file_vtab.cc
Normal file
333
src/static_file_vtab.cc
Normal file
@ -0,0 +1,333 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "static_file_vtab.hh"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base/auto_mem.hh"
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "base/paths.hh"
|
||||
#include "config.h"
|
||||
#include "ghc/filesystem.hpp"
|
||||
#include "lnav.hh"
|
||||
#include "vtab_module.hh"
|
||||
|
||||
const char* const STATIC_FILE_CREATE_STMT = R"(
|
||||
-- Access static files in the lnav configuration directories
|
||||
CREATE TABLE lnav_static_files (
|
||||
name TEXT PRIMARY KEY,
|
||||
filepath TEXT,
|
||||
content BLOB HIDDEN
|
||||
);
|
||||
)";
|
||||
|
||||
struct vtab {
|
||||
sqlite3_vtab base;
|
||||
sqlite3* db;
|
||||
};
|
||||
|
||||
struct static_file_info {
|
||||
ghc::filesystem::path sfi_path;
|
||||
};
|
||||
|
||||
struct vtab_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
std::map<std::string, static_file_info>::iterator vc_files_iter;
|
||||
std::map<std::string, static_file_info> vc_files;
|
||||
};
|
||||
|
||||
static int sfvt_destructor(sqlite3_vtab* p_svt);
|
||||
|
||||
static int
|
||||
sfvt_create(sqlite3* db,
|
||||
void* pAux,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
sqlite3_vtab** pp_vt,
|
||||
char** pzErr)
|
||||
{
|
||||
vtab* p_vt;
|
||||
|
||||
/* Allocate the sqlite3_vtab/vtab structure itself */
|
||||
p_vt = (vtab*) sqlite3_malloc(sizeof(*p_vt));
|
||||
|
||||
if (p_vt == nullptr) {
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
memset(&p_vt->base, 0, sizeof(sqlite3_vtab));
|
||||
p_vt->db = db;
|
||||
|
||||
*pp_vt = &p_vt->base;
|
||||
|
||||
int rc = sqlite3_declare_vtab(db, STATIC_FILE_CREATE_STMT);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_destructor(sqlite3_vtab* p_svt)
|
||||
{
|
||||
vtab* p_vt = (vtab*) p_svt;
|
||||
|
||||
/* Free the SQLite structure */
|
||||
sqlite3_free(p_vt);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_connect(sqlite3* db,
|
||||
void* p_aux,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
sqlite3_vtab** pp_vt,
|
||||
char** pzErr)
|
||||
{
|
||||
return sfvt_create(db, p_aux, argc, argv, pp_vt, pzErr);
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_disconnect(sqlite3_vtab* pVtab)
|
||||
{
|
||||
return sfvt_destructor(pVtab);
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_destroy(sqlite3_vtab* p_vt)
|
||||
{
|
||||
return sfvt_destructor(p_vt);
|
||||
}
|
||||
|
||||
static int sfvt_next(sqlite3_vtab_cursor* cur);
|
||||
|
||||
static void
|
||||
find_static_files(vtab_cursor* p_cur, const ghc::filesystem::path& dir)
|
||||
{
|
||||
auto& file_map = p_cur->vc_files;
|
||||
std::error_code ec;
|
||||
|
||||
for (const auto& format_dir_entry :
|
||||
ghc::filesystem::directory_iterator(dir, ec))
|
||||
{
|
||||
if (!format_dir_entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
auto format_static_files_dir = format_dir_entry.path() / "static-files";
|
||||
log_debug("format static files: %s", format_static_files_dir.c_str());
|
||||
for (const auto& static_file_entry :
|
||||
ghc::filesystem::recursive_directory_iterator(
|
||||
format_static_files_dir, ec))
|
||||
{
|
||||
auto rel_path = ghc::filesystem::relative(static_file_entry.path(),
|
||||
format_static_files_dir);
|
||||
|
||||
file_map[rel_path.string()] = {static_file_entry.path()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_open(sqlite3_vtab* p_svt, sqlite3_vtab_cursor** pp_cursor)
|
||||
{
|
||||
vtab* p_vt = (vtab*) p_svt;
|
||||
|
||||
p_vt->base.zErrMsg = NULL;
|
||||
|
||||
vtab_cursor* p_cur = (vtab_cursor*) new vtab_cursor();
|
||||
|
||||
if (p_cur == nullptr) {
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
*pp_cursor = (sqlite3_vtab_cursor*) p_cur;
|
||||
|
||||
p_cur->base.pVtab = p_svt;
|
||||
|
||||
for (const auto& config_path : lnav_data.ld_config_paths) {
|
||||
auto formats_root = config_path / "formats";
|
||||
log_debug("format root: %s", formats_root.c_str());
|
||||
find_static_files(p_cur, formats_root);
|
||||
auto configs_root = config_path / "configs";
|
||||
log_debug("configs root: %s", configs_root.c_str());
|
||||
find_static_files(p_cur, configs_root);
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_close(sqlite3_vtab_cursor* cur)
|
||||
{
|
||||
vtab_cursor* p_cur = (vtab_cursor*) cur;
|
||||
|
||||
p_cur->vc_files_iter = p_cur->vc_files.end();
|
||||
/* Free cursor struct. */
|
||||
delete p_cur;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_eof(sqlite3_vtab_cursor* cur)
|
||||
{
|
||||
vtab_cursor* vc = (vtab_cursor*) cur;
|
||||
|
||||
return vc->vc_files_iter == vc->vc_files.end();
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_next(sqlite3_vtab_cursor* cur)
|
||||
{
|
||||
vtab_cursor* vc = (vtab_cursor*) cur;
|
||||
|
||||
if (vc->vc_files_iter != vc->vc_files.end()) {
|
||||
++vc->vc_files_iter;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col)
|
||||
{
|
||||
vtab_cursor* vc = (vtab_cursor*) cur;
|
||||
|
||||
switch (col) {
|
||||
case 0:
|
||||
to_sqlite(ctx, vc->vc_files_iter->first);
|
||||
break;
|
||||
case 1: {
|
||||
sqlite3_result_text(ctx,
|
||||
vc->vc_files_iter->second.sfi_path.c_str(),
|
||||
-1,
|
||||
SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
auto read_res = lnav::filesystem::read_file(
|
||||
vc->vc_files_iter->second.sfi_path);
|
||||
if (read_res.isErr()) {
|
||||
auto um = lnav::console::user_message::error(
|
||||
"unable to read static file")
|
||||
.with_reason(read_res.unwrapErr());
|
||||
|
||||
to_sqlite(ctx, um);
|
||||
} else {
|
||||
auto str = read_res.unwrap();
|
||||
|
||||
sqlite3_result_blob(
|
||||
ctx, str.c_str(), str.size(), SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_rowid(sqlite3_vtab_cursor* cur, sqlite_int64* p_rowid)
|
||||
{
|
||||
vtab_cursor* p_cur = (vtab_cursor*) cur;
|
||||
|
||||
*p_rowid = std::distance(p_cur->vc_files.begin(), p_cur->vc_files_iter);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info)
|
||||
{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sfvt_filter(sqlite3_vtab_cursor* cur,
|
||||
int idxNum,
|
||||
const char* idxStr,
|
||||
int argc,
|
||||
sqlite3_value** argv)
|
||||
{
|
||||
vtab_cursor* p_cur = (vtab_cursor*) cur;
|
||||
|
||||
p_cur->vc_files_iter = p_cur->vc_files.begin();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static sqlite3_module static_file_vtab_module = {
|
||||
0, /* iVersion */
|
||||
sfvt_create, /* xCreate - create a vtable */
|
||||
sfvt_connect, /* xConnect - associate a vtable with a connection */
|
||||
sfvt_best_index, /* xBestIndex - best index */
|
||||
sfvt_disconnect, /* xDisconnect - disassociate a vtable with a connection
|
||||
*/
|
||||
sfvt_destroy, /* xDestroy - destroy a vtable */
|
||||
sfvt_open, /* xOpen - open a cursor */
|
||||
sfvt_close, /* xClose - close a cursor */
|
||||
sfvt_filter, /* xFilter - configure scan constraints */
|
||||
sfvt_next, /* xNext - advance a cursor */
|
||||
sfvt_eof, /* xEof - inidicate end of result set*/
|
||||
sfvt_column, /* xColumn - read data */
|
||||
sfvt_rowid, /* xRowid - read data */
|
||||
nullptr, /* xUpdate - write data */
|
||||
nullptr, /* xBegin - begin transaction */
|
||||
nullptr, /* xSync - sync transaction */
|
||||
nullptr, /* xCommit - commit transaction */
|
||||
nullptr, /* xRollback - rollback transaction */
|
||||
nullptr, /* xFindFunction - function overloading */
|
||||
};
|
||||
|
||||
int
|
||||
register_static_file_vtab(sqlite3* db)
|
||||
{
|
||||
auto_mem<char, sqlite3_free> errmsg;
|
||||
int rc;
|
||||
rc = sqlite3_create_module(
|
||||
db, "lnav_static_file_vtab_impl", &static_file_vtab_module, nullptr);
|
||||
ensure(rc == SQLITE_OK);
|
||||
if ((rc = sqlite3_exec(db,
|
||||
"CREATE VIRTUAL TABLE lnav_static_files USING "
|
||||
"lnav_static_file_vtab_impl()",
|
||||
nullptr,
|
||||
nullptr,
|
||||
errmsg.out()))
|
||||
!= SQLITE_OK)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"unable to create lnav_static_file table %s\n",
|
||||
errmsg.in());
|
||||
}
|
||||
return rc;
|
||||
}
|
39
src/static_file_vtab.hh
Normal file
39
src/static_file_vtab.hh
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef static_file_vtab_hh
|
||||
#define static_file_vtab_hh
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
int register_static_file_vtab(sqlite3* db);
|
||||
|
||||
extern const char* const STATIC_FILE_CREATE_STMT;
|
||||
|
||||
#endif
|
127
src/styling.hh
127
src/styling.hh
@ -39,6 +39,7 @@
|
||||
#include "base/result.h"
|
||||
#include "log_level.hh"
|
||||
#include "mapbox/variant.hpp"
|
||||
#include "yajlpp/yajlpp.hh"
|
||||
|
||||
struct rgb_color {
|
||||
static Result<rgb_color, std::string> from_str(const string_fragment& sf);
|
||||
@ -121,17 +122,13 @@ struct term_color_palette {
|
||||
|
||||
namespace styling {
|
||||
|
||||
struct semantic {
|
||||
};
|
||||
struct semantic {};
|
||||
|
||||
class color_unit {
|
||||
public:
|
||||
static Result<color_unit, std::string> from_str(const string_fragment& sf);
|
||||
|
||||
static color_unit make_empty()
|
||||
{
|
||||
return color_unit{rgb_color{}};
|
||||
}
|
||||
static color_unit make_empty() { return color_unit{rgb_color{}}; }
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
@ -164,65 +161,65 @@ struct highlighter_config {
|
||||
|
||||
struct lnav_theme {
|
||||
std::map<std::string, std::string> lt_vars;
|
||||
style_config lt_style_identifier;
|
||||
style_config lt_style_text;
|
||||
style_config lt_style_alt_text;
|
||||
style_config lt_style_ok;
|
||||
style_config lt_style_info;
|
||||
style_config lt_style_error;
|
||||
style_config lt_style_warning;
|
||||
style_config lt_style_popup;
|
||||
style_config lt_style_focused;
|
||||
style_config lt_style_disabled_focused;
|
||||
style_config lt_style_scrollbar;
|
||||
style_config lt_style_hidden;
|
||||
style_config lt_style_adjusted_time;
|
||||
style_config lt_style_skewed_time;
|
||||
style_config lt_style_offset_time;
|
||||
style_config lt_style_invalid_msg;
|
||||
style_config lt_style_status_title;
|
||||
style_config lt_style_status_title_hotkey;
|
||||
style_config lt_style_status_disabled_title;
|
||||
style_config lt_style_status_subtitle;
|
||||
style_config lt_style_status_info;
|
||||
style_config lt_style_status_hotkey;
|
||||
style_config lt_style_quoted_code;
|
||||
style_config lt_style_code_border;
|
||||
style_config lt_style_keyword;
|
||||
style_config lt_style_string;
|
||||
style_config lt_style_comment;
|
||||
style_config lt_style_doc_directive;
|
||||
style_config lt_style_variable;
|
||||
style_config lt_style_symbol;
|
||||
style_config lt_style_number;
|
||||
style_config lt_style_re_special;
|
||||
style_config lt_style_re_repeat;
|
||||
style_config lt_style_diff_delete;
|
||||
style_config lt_style_diff_add;
|
||||
style_config lt_style_diff_section;
|
||||
style_config lt_style_low_threshold;
|
||||
style_config lt_style_med_threshold;
|
||||
style_config lt_style_high_threshold;
|
||||
style_config lt_style_status;
|
||||
style_config lt_style_warn_status;
|
||||
style_config lt_style_alert_status;
|
||||
style_config lt_style_active_status;
|
||||
style_config lt_style_inactive_status;
|
||||
style_config lt_style_inactive_alert_status;
|
||||
style_config lt_style_file;
|
||||
style_config lt_style_header[6];
|
||||
style_config lt_style_hr;
|
||||
style_config lt_style_hyperlink;
|
||||
style_config lt_style_list_glyph;
|
||||
style_config lt_style_breadcrumb;
|
||||
style_config lt_style_table_border;
|
||||
style_config lt_style_table_header;
|
||||
style_config lt_style_quote_border;
|
||||
style_config lt_style_quoted_text;
|
||||
style_config lt_style_footnote_border;
|
||||
style_config lt_style_footnote_text;
|
||||
style_config lt_style_snippet_border;
|
||||
std::map<log_level_t, style_config> lt_level_styles;
|
||||
positioned_property<style_config> lt_style_identifier;
|
||||
positioned_property<style_config> lt_style_text;
|
||||
positioned_property<style_config> lt_style_alt_text;
|
||||
positioned_property<style_config> lt_style_ok;
|
||||
positioned_property<style_config> lt_style_info;
|
||||
positioned_property<style_config> lt_style_error;
|
||||
positioned_property<style_config> lt_style_warning;
|
||||
positioned_property<style_config> lt_style_popup;
|
||||
positioned_property<style_config> lt_style_focused;
|
||||
positioned_property<style_config> lt_style_disabled_focused;
|
||||
positioned_property<style_config> lt_style_scrollbar;
|
||||
positioned_property<style_config> lt_style_hidden;
|
||||
positioned_property<style_config> lt_style_adjusted_time;
|
||||
positioned_property<style_config> lt_style_skewed_time;
|
||||
positioned_property<style_config> lt_style_offset_time;
|
||||
positioned_property<style_config> lt_style_invalid_msg;
|
||||
positioned_property<style_config> lt_style_status_title;
|
||||
positioned_property<style_config> lt_style_status_title_hotkey;
|
||||
positioned_property<style_config> lt_style_status_disabled_title;
|
||||
positioned_property<style_config> lt_style_status_subtitle;
|
||||
positioned_property<style_config> lt_style_status_info;
|
||||
positioned_property<style_config> lt_style_status_hotkey;
|
||||
positioned_property<style_config> lt_style_quoted_code;
|
||||
positioned_property<style_config> lt_style_code_border;
|
||||
positioned_property<style_config> lt_style_keyword;
|
||||
positioned_property<style_config> lt_style_string;
|
||||
positioned_property<style_config> lt_style_comment;
|
||||
positioned_property<style_config> lt_style_doc_directive;
|
||||
positioned_property<style_config> lt_style_variable;
|
||||
positioned_property<style_config> lt_style_symbol;
|
||||
positioned_property<style_config> lt_style_number;
|
||||
positioned_property<style_config> lt_style_re_special;
|
||||
positioned_property<style_config> lt_style_re_repeat;
|
||||
positioned_property<style_config> lt_style_diff_delete;
|
||||
positioned_property<style_config> lt_style_diff_add;
|
||||
positioned_property<style_config> lt_style_diff_section;
|
||||
positioned_property<style_config> lt_style_low_threshold;
|
||||
positioned_property<style_config> lt_style_med_threshold;
|
||||
positioned_property<style_config> lt_style_high_threshold;
|
||||
positioned_property<style_config> lt_style_status;
|
||||
positioned_property<style_config> lt_style_warn_status;
|
||||
positioned_property<style_config> lt_style_alert_status;
|
||||
positioned_property<style_config> lt_style_active_status;
|
||||
positioned_property<style_config> lt_style_inactive_status;
|
||||
positioned_property<style_config> lt_style_inactive_alert_status;
|
||||
positioned_property<style_config> lt_style_file;
|
||||
positioned_property<style_config> lt_style_header[6];
|
||||
positioned_property<style_config> lt_style_hr;
|
||||
positioned_property<style_config> lt_style_hyperlink;
|
||||
positioned_property<style_config> lt_style_list_glyph;
|
||||
positioned_property<style_config> lt_style_breadcrumb;
|
||||
positioned_property<style_config> lt_style_table_border;
|
||||
positioned_property<style_config> lt_style_table_header;
|
||||
positioned_property<style_config> lt_style_quote_border;
|
||||
positioned_property<style_config> lt_style_quoted_text;
|
||||
positioned_property<style_config> lt_style_footnote_border;
|
||||
positioned_property<style_config> lt_style_footnote_text;
|
||||
positioned_property<style_config> lt_style_snippet_border;
|
||||
std::map<log_level_t, positioned_property<style_config>> lt_level_styles;
|
||||
std::map<std::string, highlighter_config> lt_highlights;
|
||||
};
|
||||
|
||||
|
@ -349,7 +349,7 @@ tailer::looper::host_tailer::for_host(const std::string& netloc)
|
||||
}
|
||||
|
||||
std::vector<std::string> error_queue;
|
||||
log_debug("starting err reader");
|
||||
log_debug("tailer(%s): starting err reader", netloc.c_str());
|
||||
std::thread err_reader([netloc,
|
||||
err = std::move(err_pipe.read_end()),
|
||||
&error_queue]() mutable {
|
||||
@ -358,25 +358,30 @@ tailer::looper::host_tailer::for_host(const std::string& netloc)
|
||||
read_err_pipe(netloc, err, error_queue);
|
||||
});
|
||||
|
||||
log_debug("writing to child");
|
||||
log_debug("tailer(%s): writing to child", netloc.c_str());
|
||||
auto sf = tailer_bin[0].to_string_fragment();
|
||||
ssize_t total_bytes = 0;
|
||||
bool write_failed = false;
|
||||
|
||||
while (total_bytes < sf.length()) {
|
||||
log_debug("attempting to write %d", sf.length() - total_bytes);
|
||||
auto rc = write(
|
||||
in_pipe.write_end(), sf.data(), sf.length() - total_bytes);
|
||||
|
||||
log_debug("wrote %d", rc);
|
||||
if (rc < 0) {
|
||||
log_error(" tailer(%s): write failed -- %s",
|
||||
netloc.c_str(),
|
||||
strerror(errno));
|
||||
write_failed = true;
|
||||
break;
|
||||
}
|
||||
log_debug(" wrote %d", rc);
|
||||
total_bytes += rc;
|
||||
}
|
||||
|
||||
in_pipe.write_end().reset();
|
||||
|
||||
while (true) {
|
||||
while (!write_failed) {
|
||||
char buffer[1024];
|
||||
|
||||
auto rc = read(out_pipe.read_end(), buffer, sizeof(buffer));
|
||||
@ -1142,6 +1147,7 @@ tailer::looper::remote_path_queue::send_synced_to_main(
|
||||
void
|
||||
tailer::looper::report_error(std::string path, std::string msg)
|
||||
{
|
||||
log_error("reporting error: %s -- %s", path.c_str(), msg.c_str());
|
||||
isc::to<main_looper&, services::main_t>().send([=](auto& mlooper) {
|
||||
file_collection fc;
|
||||
|
||||
|
@ -42,16 +42,17 @@
|
||||
|
||||
enum class text_format_t {
|
||||
TF_UNKNOWN,
|
||||
TF_LOG,
|
||||
TF_PYTHON,
|
||||
TF_RUST,
|
||||
TF_JAVA,
|
||||
TF_C_LIKE,
|
||||
TF_SQL,
|
||||
TF_XML,
|
||||
TF_JAVA,
|
||||
TF_JSON,
|
||||
TF_LOG,
|
||||
TF_MAN,
|
||||
TF_MARKDOWN,
|
||||
TF_PYTHON,
|
||||
TF_RUST,
|
||||
TF_SQL,
|
||||
TF_XML,
|
||||
TF_YAML,
|
||||
};
|
||||
|
||||
namespace fmt {
|
||||
@ -95,6 +96,9 @@ struct formatter<text_format_t> : formatter<string_view> {
|
||||
case text_format_t::TF_MARKDOWN:
|
||||
name = "text/markdown";
|
||||
break;
|
||||
case text_format_t::TF_YAML:
|
||||
name = "application/yaml";
|
||||
break;
|
||||
}
|
||||
return formatter<string_view>::format(name, ctx);
|
||||
}
|
||||
|
@ -31,9 +31,13 @@
|
||||
|
||||
#include "base/ansi_scrubber.hh"
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "base/itertools.hh"
|
||||
#include "bound_tags.hh"
|
||||
#include "config.h"
|
||||
#include "lnav.events.hh"
|
||||
#include "md2attr_line.hh"
|
||||
#include "sqlitepp.hh"
|
||||
|
||||
using namespace lnav::roles::literals;
|
||||
|
||||
@ -439,6 +443,10 @@ textfile_sub_source::rescan_files(
|
||||
textfile_sub_source::scan_callback& callback,
|
||||
nonstd::optional<ui_clock::time_point> deadline)
|
||||
{
|
||||
static auto& lnav_db
|
||||
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
|
||||
sqlite_db_tag>();
|
||||
|
||||
file_iterator iter;
|
||||
bool retval = false;
|
||||
|
||||
@ -502,6 +510,7 @@ textfile_sub_source::rescan_files(
|
||||
if (read_res.isOk()) {
|
||||
auto content = read_res.unwrap();
|
||||
auto content_sf = string_fragment{content};
|
||||
std::string frontmatter;
|
||||
auto front_matter_terminator = content.length() > 8
|
||||
? content.find("\n---\n", 4)
|
||||
: std::string::npos;
|
||||
@ -509,6 +518,8 @@ textfile_sub_source::rescan_files(
|
||||
if (startswith(content, "---\n")
|
||||
&& front_matter_terminator != std::string::npos)
|
||||
{
|
||||
frontmatter
|
||||
= content.substr(4, front_matter_terminator - 3);
|
||||
content_sf
|
||||
= content_sf.substr(front_matter_terminator + 4);
|
||||
}
|
||||
@ -524,7 +535,21 @@ textfile_sub_source::rescan_files(
|
||||
rf.rf_text_source = std::make_unique<plain_text_source>();
|
||||
rf.rf_text_source->register_view(this->tss_view);
|
||||
if (parse_res.isOk()) {
|
||||
auto& lf_meta = lf->get_embedded_metadata();
|
||||
|
||||
rf.rf_text_source->replace_with(parse_res.unwrap());
|
||||
|
||||
if (!frontmatter.empty()) {
|
||||
lf_meta["net.daringfireball.markdown.frontmatter"]
|
||||
= {text_format_t::TF_YAML, frontmatter};
|
||||
}
|
||||
|
||||
lnav::events::publish(
|
||||
lnav_db,
|
||||
lnav::events::file::format_detected{
|
||||
lf->get_filename(),
|
||||
fmt::to_string(lf->get_text_format()),
|
||||
});
|
||||
} else {
|
||||
auto view_content
|
||||
= lnav::console::user_message::error(
|
||||
@ -637,3 +662,147 @@ textfile_sub_source::quiesce()
|
||||
lf->quiesce();
|
||||
}
|
||||
}
|
||||
|
||||
nonstd::optional<vis_line_t>
|
||||
textfile_sub_source::row_for_anchor(const std::string& id)
|
||||
{
|
||||
auto lf = this->current_file();
|
||||
if (!lf) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
auto rend_iter = this->tss_rendered_files.find(lf->get_filename());
|
||||
if (rend_iter != this->tss_rendered_files.end()) {
|
||||
return rend_iter->second.rf_text_source->row_for_anchor(id);
|
||||
}
|
||||
|
||||
auto iter = this->tss_doc_metadata.find(lf->get_filename());
|
||||
if (iter == this->tss_doc_metadata.end()) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
const auto& meta = iter->second.ms_metadata;
|
||||
nonstd::optional<vis_line_t> retval;
|
||||
|
||||
lnav::document::hier_node::depth_first(
|
||||
meta.m_sections_root.get(),
|
||||
[lf, &id, &retval](const lnav::document::hier_node* node) {
|
||||
for (const auto& child_pair : node->hn_named_children) {
|
||||
auto child_anchor
|
||||
= text_anchors::to_anchor_string(child_pair.first);
|
||||
|
||||
if (child_anchor == id) {
|
||||
auto ll_opt
|
||||
= lf->line_for_offset(child_pair.second->hn_start);
|
||||
if (ll_opt != lf->end()) {
|
||||
retval = vis_line_t(
|
||||
std::distance(lf->cbegin(), ll_opt.value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string>
|
||||
textfile_sub_source::get_anchors()
|
||||
{
|
||||
std::unordered_set<std::string> retval;
|
||||
|
||||
auto lf = this->current_file();
|
||||
if (!lf) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
auto rend_iter = this->tss_rendered_files.find(lf->get_filename());
|
||||
if (rend_iter != this->tss_rendered_files.end()) {
|
||||
return rend_iter->second.rf_text_source->get_anchors();
|
||||
}
|
||||
|
||||
auto iter = this->tss_doc_metadata.find(lf->get_filename());
|
||||
if (iter == this->tss_doc_metadata.end()) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
const auto& meta = iter->second.ms_metadata;
|
||||
|
||||
lnav::document::hier_node::depth_first(
|
||||
meta.m_sections_root.get(),
|
||||
[&retval](const lnav::document::hier_node* node) {
|
||||
if (retval.size() > 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& child_pair : node->hn_named_children) {
|
||||
retval.emplace(
|
||||
text_anchors::to_anchor_string(child_pair.first));
|
||||
}
|
||||
});
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
nonstd::optional<std::string>
|
||||
textfile_sub_source::anchor_for_row(vis_line_t vl)
|
||||
{
|
||||
nonstd::optional<std::string> retval;
|
||||
|
||||
auto lf = this->current_file();
|
||||
if (!lf) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
auto rend_iter = this->tss_rendered_files.find(lf->get_filename());
|
||||
if (rend_iter != this->tss_rendered_files.end()) {
|
||||
return rend_iter->second.rf_text_source->anchor_for_row(vl);
|
||||
}
|
||||
|
||||
auto iter = this->tss_doc_metadata.find(lf->get_filename());
|
||||
if (iter == this->tss_doc_metadata.end()) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
auto* lfo = dynamic_cast<line_filter_observer*>(lf->get_logline_observer());
|
||||
auto ll_iter = lf->begin() + lfo->lfo_filter_state.tfs_index[vl];
|
||||
auto ll_next_iter = ll_iter + 1;
|
||||
auto end_offset = (ll_next_iter == lf->end())
|
||||
? lf->get_index_size() - 1
|
||||
: ll_next_iter->get_offset() - 1;
|
||||
iter->second.ms_metadata.m_sections_tree.visit_overlapping(
|
||||
ll_iter->get_offset(),
|
||||
end_offset,
|
||||
[&retval](const lnav::document::section_interval_t& iv) {
|
||||
retval = iv.value.match(
|
||||
[](const std::string& str) {
|
||||
return nonstd::make_optional(
|
||||
text_anchors::to_anchor_string(str));
|
||||
},
|
||||
[](size_t) { return nonstd::nullopt; });
|
||||
});
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
textfile_sub_source::to_front(const std::string& filename)
|
||||
{
|
||||
auto lf_opt = this->tss_files
|
||||
| lnav::itertools::find_if([&filename](const auto& elem) {
|
||||
return elem->get_filename() == filename;
|
||||
});
|
||||
if (!lf_opt) {
|
||||
lf_opt = this->tss_hidden_files
|
||||
| lnav::itertools::find_if([&filename](const auto& elem) {
|
||||
return elem->get_filename() == filename;
|
||||
});
|
||||
}
|
||||
|
||||
if (!lf_opt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->to_front(*(lf_opt.value()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -40,7 +40,8 @@
|
||||
|
||||
class textfile_sub_source
|
||||
: public text_sub_source
|
||||
, public vis_location_history {
|
||||
, public vis_location_history
|
||||
, public text_anchors {
|
||||
public:
|
||||
using file_iterator = std::deque<std::shared_ptr<logfile>>::iterator;
|
||||
|
||||
@ -94,6 +95,8 @@ public:
|
||||
|
||||
void to_front(const std::shared_ptr<logfile>& lf);
|
||||
|
||||
bool to_front(const std::string& filename);
|
||||
|
||||
void set_top_from_off(file_off_t off);
|
||||
|
||||
void rotate_left();
|
||||
@ -133,6 +136,12 @@ public:
|
||||
void text_crumbs_for_line(int line,
|
||||
std::vector<breadcrumb::crumb>& crumbs) override;
|
||||
|
||||
nonstd::optional<vis_line_t> row_for_anchor(const std::string& id) override;
|
||||
|
||||
nonstd::optional<std::string> anchor_for_row(vis_line_t vl) override;
|
||||
|
||||
std::unordered_set<std::string> get_anchors() override;
|
||||
|
||||
void quiesce() override;
|
||||
|
||||
private:
|
||||
|
@ -644,13 +644,16 @@ textview_curses::execute_search(const std::string& regex_orig)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
textview_curses::horiz_shift(vis_line_t start,
|
||||
vis_line_t end,
|
||||
int off_start,
|
||||
std::pair<int, int>& range_out)
|
||||
nonstd::optional<std::pair<int, int>>
|
||||
textview_curses::horiz_shift(vis_line_t start, vis_line_t end, int off_start)
|
||||
{
|
||||
auto& hl = this->tc_highlights[{highlight_source_t::PREVIEW, "search"}];
|
||||
auto hl_iter
|
||||
= this->tc_highlights.find({highlight_source_t::PREVIEW, "search"});
|
||||
if (hl_iter == this->tc_highlights.end()
|
||||
|| hl_iter->second.h_regex == nullptr)
|
||||
{
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
int prev_hit = -1, next_hit = INT_MAX;
|
||||
|
||||
for (; start < end; ++start) {
|
||||
@ -660,7 +663,7 @@ textview_curses::horiz_shift(vis_line_t start,
|
||||
const auto& str = rows[0].get_string();
|
||||
pcre_context_static<60> pc;
|
||||
pcre_input pi(str);
|
||||
while (hl.h_regex->match(pc, pi)) {
|
||||
while (hl_iter->second.h_regex->match(pc, pi)) {
|
||||
if (pc.all()->c_begin < off_start) {
|
||||
prev_hit = std::max(prev_hit, pc.all()->c_begin);
|
||||
} else if (pc.all()->c_begin > off_start) {
|
||||
@ -669,7 +672,10 @@ textview_curses::horiz_shift(vis_line_t start,
|
||||
}
|
||||
}
|
||||
|
||||
range_out = std::make_pair(prev_hit, next_hit);
|
||||
if (prev_hit == -1 && next_hit == INT_MAX) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
return std::make_pair(prev_hit, next_hit);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1117,3 +1123,12 @@ logfile_filter_state::content_line_to_vis_line(uint32_t line)
|
||||
|
||||
return nonstd::make_optional(std::distance(this->tfs_index.begin(), iter));
|
||||
}
|
||||
|
||||
std::string
|
||||
text_anchors::to_anchor_string(const std::string& raw)
|
||||
{
|
||||
static const pcrepp ANCHOR_RE(R"([^\w]+)");
|
||||
|
||||
return fmt::format(FMT_STRING("#{}"),
|
||||
ANCHOR_RE.replace(tolower(raw).c_str(), "-"));
|
||||
}
|
||||
|
@ -237,6 +237,20 @@ protected:
|
||||
};
|
||||
};
|
||||
|
||||
class text_anchors {
|
||||
public:
|
||||
virtual ~text_anchors() = default;
|
||||
|
||||
static std::string to_anchor_string(const std::string& raw);
|
||||
|
||||
virtual nonstd::optional<vis_line_t> row_for_anchor(const std::string& id)
|
||||
= 0;
|
||||
|
||||
virtual nonstd::optional<std::string> anchor_for_row(vis_line_t vl) = 0;
|
||||
|
||||
virtual std::unordered_set<std::string> get_anchors() = 0;
|
||||
};
|
||||
|
||||
class location_history {
|
||||
public:
|
||||
virtual ~location_history() = default;
|
||||
@ -514,15 +528,14 @@ public:
|
||||
return this->tc_delegate;
|
||||
}
|
||||
|
||||
void horiz_shift(vis_line_t start,
|
||||
vis_line_t end,
|
||||
int off_start,
|
||||
std::pair<int, int>& range_out);
|
||||
nonstd::optional<std::pair<int, int>> horiz_shift(vis_line_t start,
|
||||
vis_line_t end,
|
||||
int off_start);
|
||||
|
||||
void set_search_action(action sa)
|
||||
{
|
||||
this->tc_search_action = std::move(sa);
|
||||
};
|
||||
}
|
||||
|
||||
void grep_end_batch(grep_proc<vis_line_t>& gp);
|
||||
void grep_end(grep_proc<vis_line_t>& gp);
|
||||
|
30945
src/third-party/rapidyaml/ryml_all.hpp
vendored
Normal file
30945
src/third-party/rapidyaml/ryml_all.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -49,6 +49,7 @@ TIME_FORMATS = \
|
||||
"%m/%d/%Y %H:%M:%S" \
|
||||
"%d/%b/%y %H:%M:%S" \
|
||||
"%m%d %H:%M:%S" \
|
||||
"%Y%m%d %H:%M:%S" \
|
||||
"%Y%m%d.%H%M%S" \
|
||||
"%H:%M:%S" \
|
||||
"%M:%S" \
|
||||
|
@ -325,7 +325,8 @@ view_curses::mvwattrline(WINDOW* window,
|
||||
} else if (iter->sa_type == &VC_STYLE) {
|
||||
attrs = iter->sa_value.get<text_attrs>();
|
||||
} else if (iter->sa_type == &SA_LEVEL) {
|
||||
attrs = vc.vc_level_attrs[iter->sa_value.get<int64_t>()].first;
|
||||
attrs = vc.attrs_for_level(
|
||||
(log_level_t) iter->sa_value.get<int64_t>());
|
||||
} else if (iter->sa_type == &VC_ROLE) {
|
||||
auto role = iter->sa_value.get<role_t>();
|
||||
attrs = vc.attrs_for_role(role);
|
||||
@ -581,13 +582,26 @@ attr_for_colors(nonstd::optional<short> fg, nonstd::optional<short> bg)
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::pair<text_attrs, text_attrs>
|
||||
view_colors::role_attrs
|
||||
view_colors::to_attrs(const lnav_theme& lt,
|
||||
const style_config& sc,
|
||||
const style_config& fallback_sc,
|
||||
const positioned_property<style_config>& pp_sc,
|
||||
const positioned_property<style_config>& pp_fallback_sc,
|
||||
lnav_config_listener::error_reporter& reporter)
|
||||
{
|
||||
const auto& sc = pp_sc.pp_value;
|
||||
const auto& fallback_sc = pp_fallback_sc.pp_value;
|
||||
std::string fg1, bg1, fg_color, bg_color;
|
||||
intern_string_t role_class;
|
||||
|
||||
if (!pp_sc.pp_path.empty()) {
|
||||
auto role_class_path
|
||||
= ghc::filesystem::path(pp_sc.pp_path.to_string()).parent_path();
|
||||
auto inner = role_class_path.filename().string();
|
||||
auto outer = role_class_path.parent_path().filename().string();
|
||||
|
||||
role_class = intern_string::lookup(
|
||||
fmt::format(FMT_STRING("-lnav_{}_{}"), outer, inner));
|
||||
}
|
||||
|
||||
fg1 = sc.sc_color;
|
||||
if (fg1.empty()) {
|
||||
@ -629,7 +643,7 @@ view_colors::to_attrs(const lnav_theme& lt,
|
||||
retval2.ta_attrs |= A_BOLD;
|
||||
}
|
||||
|
||||
return {retval1, retval2};
|
||||
return {retval1, retval2, role_class};
|
||||
}
|
||||
|
||||
void
|
||||
@ -695,14 +709,17 @@ view_colors::init_roles(const lnav_theme& lt,
|
||||
|
||||
if (lnav_config.lc_ui_dim_text) {
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_TEXT)]
|
||||
.first.ta_attrs
|
||||
.ra_normal.ta_attrs
|
||||
|= A_DIM;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_TEXT)]
|
||||
.second.ta_attrs
|
||||
.ra_reverse.ta_attrs
|
||||
|= A_DIM;
|
||||
}
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_SEARCH)]
|
||||
= std::make_pair(text_attrs{A_REVERSE}, text_attrs{A_REVERSE});
|
||||
= role_attrs{text_attrs{A_REVERSE}, text_attrs{A_REVERSE}};
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_SEARCH)]
|
||||
.ra_class_name
|
||||
= intern_string::lookup("-lnav_styles_search");
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_IDENTIFIER)]
|
||||
= this->to_attrs(
|
||||
lt, lt.lt_style_identifier, lt.lt_style_text, reporter);
|
||||
@ -743,17 +760,19 @@ view_colors::init_roles(const lnav_theme& lt,
|
||||
= this->to_attrs(
|
||||
lt, lt.lt_style_active_status, lt.lt_style_status, reporter);
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_ACTIVE_STATUS2)]
|
||||
= std::make_pair(this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_ACTIVE_STATUS)]
|
||||
.first,
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_ACTIVE_STATUS)]
|
||||
.second);
|
||||
= role_attrs{
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_ACTIVE_STATUS)]
|
||||
.ra_normal,
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_ACTIVE_STATUS)]
|
||||
.ra_reverse,
|
||||
};
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_ACTIVE_STATUS2)]
|
||||
.first.ta_attrs
|
||||
.ra_normal.ta_attrs
|
||||
|= A_BOLD;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_ACTIVE_STATUS2)]
|
||||
.second.ta_attrs
|
||||
.ra_reverse.ta_attrs
|
||||
|= A_BOLD;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(role_t::VCR_STATUS_TITLE)]
|
||||
= this->to_attrs(
|
||||
@ -824,61 +843,69 @@ view_colors::init_roles(const lnav_theme& lt,
|
||||
lt, lt.lt_style_snippet_border, lt.lt_style_text, reporter);
|
||||
|
||||
{
|
||||
style_config stitch_sc;
|
||||
positioned_property<style_config> stitch_sc;
|
||||
|
||||
stitch_sc.sc_color = lt.lt_style_status_subtitle.sc_background_color;
|
||||
stitch_sc.sc_background_color
|
||||
= lt.lt_style_status_title.sc_background_color;
|
||||
stitch_sc.pp_value.sc_color
|
||||
= lt.lt_style_status_subtitle.pp_value.sc_background_color;
|
||||
stitch_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_status_title.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_STATUS_STITCH_TITLE_TO_SUB)]
|
||||
= this->to_attrs(lt, stitch_sc, lt.lt_style_status, reporter);
|
||||
}
|
||||
{
|
||||
style_config stitch_sc;
|
||||
positioned_property<style_config> stitch_sc;
|
||||
|
||||
stitch_sc.sc_color = lt.lt_style_status_title.sc_background_color;
|
||||
stitch_sc.sc_background_color
|
||||
= lt.lt_style_status_subtitle.sc_background_color;
|
||||
stitch_sc.pp_value.sc_color
|
||||
= lt.lt_style_status_title.pp_value.sc_background_color;
|
||||
stitch_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_status_subtitle.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_STATUS_STITCH_SUB_TO_TITLE)]
|
||||
= this->to_attrs(lt, stitch_sc, lt.lt_style_status, reporter);
|
||||
}
|
||||
|
||||
{
|
||||
style_config stitch_sc;
|
||||
positioned_property<style_config> stitch_sc;
|
||||
|
||||
stitch_sc.sc_color = lt.lt_style_status.sc_background_color;
|
||||
stitch_sc.sc_background_color
|
||||
= lt.lt_style_status_subtitle.sc_background_color;
|
||||
stitch_sc.pp_value.sc_color
|
||||
= lt.lt_style_status.pp_value.sc_background_color;
|
||||
stitch_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_status_subtitle.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_STATUS_STITCH_SUB_TO_NORMAL)]
|
||||
= this->to_attrs(lt, stitch_sc, lt.lt_style_status, reporter);
|
||||
}
|
||||
{
|
||||
style_config stitch_sc;
|
||||
positioned_property<style_config> stitch_sc;
|
||||
|
||||
stitch_sc.sc_color = lt.lt_style_status_subtitle.sc_background_color;
|
||||
stitch_sc.sc_background_color = lt.lt_style_status.sc_background_color;
|
||||
stitch_sc.pp_value.sc_color
|
||||
= lt.lt_style_status_subtitle.pp_value.sc_background_color;
|
||||
stitch_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_status.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_STATUS_STITCH_NORMAL_TO_SUB)]
|
||||
= this->to_attrs(lt, stitch_sc, lt.lt_style_status, reporter);
|
||||
}
|
||||
|
||||
{
|
||||
style_config stitch_sc;
|
||||
positioned_property<style_config> stitch_sc;
|
||||
|
||||
stitch_sc.sc_color = lt.lt_style_status.sc_background_color;
|
||||
stitch_sc.sc_background_color
|
||||
= lt.lt_style_status_title.sc_background_color;
|
||||
stitch_sc.pp_value.sc_color
|
||||
= lt.lt_style_status.pp_value.sc_background_color;
|
||||
stitch_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_status_title.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL)]
|
||||
= this->to_attrs(lt, stitch_sc, lt.lt_style_status, reporter);
|
||||
}
|
||||
{
|
||||
style_config stitch_sc;
|
||||
positioned_property<style_config> stitch_sc;
|
||||
|
||||
stitch_sc.sc_color = lt.lt_style_status_title.sc_background_color;
|
||||
stitch_sc.sc_background_color = lt.lt_style_status.sc_background_color;
|
||||
stitch_sc.pp_value.sc_color
|
||||
= lt.lt_style_status_title.pp_value.sc_background_color;
|
||||
stitch_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_status.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE)]
|
||||
= this->to_attrs(lt, stitch_sc, lt.lt_style_status, reporter);
|
||||
@ -909,19 +936,21 @@ view_colors::init_roles(const lnav_theme& lt,
|
||||
= this->to_attrs(
|
||||
lt, lt.lt_style_scrollbar, lt.lt_style_status, reporter);
|
||||
{
|
||||
style_config bar_sc;
|
||||
positioned_property<style_config> bar_sc;
|
||||
|
||||
bar_sc.sc_color = lt.lt_style_error.sc_color;
|
||||
bar_sc.sc_background_color = lt.lt_style_scrollbar.sc_background_color;
|
||||
bar_sc.pp_value.sc_color = lt.lt_style_error.pp_value.sc_color;
|
||||
bar_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_scrollbar.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_SCROLLBAR_ERROR)]
|
||||
= this->to_attrs(lt, bar_sc, lt.lt_style_alert_status, reporter);
|
||||
}
|
||||
{
|
||||
style_config bar_sc;
|
||||
positioned_property<style_config> bar_sc;
|
||||
|
||||
bar_sc.sc_color = lt.lt_style_warning.sc_color;
|
||||
bar_sc.sc_background_color = lt.lt_style_scrollbar.sc_background_color;
|
||||
bar_sc.pp_value.sc_color = lt.lt_style_warning.pp_value.sc_color;
|
||||
bar_sc.pp_value.sc_background_color
|
||||
= lt.lt_style_scrollbar.pp_value.sc_background_color;
|
||||
this->vc_role_attrs[lnav::enums::to_underlying(
|
||||
role_t::VCR_SCROLLBAR_WARNING)]
|
||||
= this->to_attrs(lt, bar_sc, lt.lt_style_warn_status, reporter);
|
||||
@ -984,7 +1013,7 @@ view_colors::init_roles(const lnav_theme& lt,
|
||||
|
||||
if (level_iter == lt.lt_level_styles.end()) {
|
||||
this->vc_level_attrs[level]
|
||||
= std::make_pair(text_attrs{}, text_attrs{});
|
||||
= role_attrs{text_attrs{}, text_attrs{}};
|
||||
} else {
|
||||
this->vc_level_attrs[level] = this->to_attrs(
|
||||
lt, level_iter->second, lt.lt_style_text, reporter);
|
||||
@ -995,6 +1024,28 @@ view_colors::init_roles(const lnav_theme& lt,
|
||||
this->vc_color_pair_end = 1;
|
||||
}
|
||||
this->vc_dyn_pairs.clear();
|
||||
|
||||
for (int32_t role_index = 0;
|
||||
role_index < lnav::enums::to_underlying(role_t::VCR__MAX);
|
||||
role_index++)
|
||||
{
|
||||
const auto& ra = this->vc_role_attrs[role_index];
|
||||
if (ra.ra_class_name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this->vc_class_to_role[ra.ra_class_name.to_string()]
|
||||
= VC_ROLE.value(role_t(role_index));
|
||||
}
|
||||
for (int level_index = 0; level_index < LEVEL__MAX; level_index++) {
|
||||
const auto& ra = this->vc_level_attrs[level_index];
|
||||
if (ra.ra_class_name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this->vc_class_to_role[ra.ra_class_name.to_string()]
|
||||
= SA_LEVEL.value(level_index);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@ -1198,12 +1249,6 @@ lab_color::operator!=(const lab_color& rhs) const
|
||||
return !(rhs == *this);
|
||||
}
|
||||
|
||||
string_attr_pair
|
||||
view_colors::roles::file()
|
||||
{
|
||||
return VC_ROLE.value(role_t::VCR_FILE);
|
||||
}
|
||||
|
||||
#include <term.h>
|
||||
|
||||
Result<screen_curses, std::string>
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/attr_line.hh"
|
||||
@ -228,16 +229,8 @@ public:
|
||||
require(role < role_t::VCR__MAX);
|
||||
|
||||
return selected
|
||||
? this->vc_role_attrs[lnav::enums::to_underlying(role)].second
|
||||
: this->vc_role_attrs[lnav::enums::to_underlying(role)].first;
|
||||
}
|
||||
|
||||
text_attrs reverse_attrs_for_role(role_t role) const
|
||||
{
|
||||
require(role > role_t::VCR_NONE);
|
||||
require(role < role_t::VCR__MAX);
|
||||
|
||||
return this->vc_role_reverse_colors[lnav::enums::to_underlying(role)];
|
||||
? this->vc_role_attrs[lnav::enums::to_underlying(role)].ra_reverse
|
||||
: this->vc_role_attrs[lnav::enums::to_underlying(role)].ra_normal;
|
||||
}
|
||||
|
||||
nonstd::optional<short> color_for_ident(const char* str, size_t len) const;
|
||||
@ -259,6 +252,11 @@ public:
|
||||
return this->attrs_for_ident(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
text_attrs attrs_for_level(log_level_t level) const
|
||||
{
|
||||
return this->vc_level_attrs[level].ra_normal;
|
||||
}
|
||||
|
||||
int ensure_color_pair(short fg, short bg);
|
||||
|
||||
int ensure_color_pair(nonstd::optional<short> fg,
|
||||
@ -272,22 +270,12 @@ public:
|
||||
|
||||
nonstd::optional<short> match_color(const styling::color_unit& color) const;
|
||||
|
||||
std::pair<text_attrs, text_attrs> to_attrs(
|
||||
const lnav_theme& lt,
|
||||
const style_config& sc,
|
||||
const style_config& fallback_sc,
|
||||
lnav_config_listener::error_reporter& reporter);
|
||||
|
||||
std::pair<text_attrs, text_attrs> vc_level_attrs[LEVEL__MAX];
|
||||
|
||||
short ansi_to_theme_color(short ansi_fg) const
|
||||
{
|
||||
return this->vc_ansi_to_theme[ansi_fg];
|
||||
}
|
||||
|
||||
struct roles {
|
||||
static string_attr_pair file();
|
||||
};
|
||||
std::unordered_map<std::string, string_attr_pair> vc_class_to_role;
|
||||
|
||||
static bool initialized;
|
||||
|
||||
@ -301,12 +289,21 @@ private:
|
||||
int dp_color_pair;
|
||||
};
|
||||
|
||||
struct role_attrs {
|
||||
text_attrs ra_normal;
|
||||
text_attrs ra_reverse;
|
||||
intern_string_t ra_class_name;
|
||||
};
|
||||
|
||||
role_attrs to_attrs(const lnav_theme& lt,
|
||||
const positioned_property<style_config>& sc,
|
||||
const positioned_property<style_config>& fallback_sc,
|
||||
lnav_config_listener::error_reporter& reporter);
|
||||
|
||||
role_attrs vc_level_attrs[LEVEL__MAX];
|
||||
|
||||
/** Map of role IDs to attribute values. */
|
||||
std::pair<text_attrs, text_attrs>
|
||||
vc_role_attrs[lnav::enums::to_underlying(role_t::VCR__MAX)];
|
||||
/** Map of role IDs to reverse-video attribute values. */
|
||||
text_attrs
|
||||
vc_role_reverse_colors[lnav::enums::to_underlying(role_t::VCR__MAX)];
|
||||
role_attrs vc_role_attrs[lnav::enums::to_underlying(role_t::VCR__MAX)];
|
||||
short vc_ansi_to_theme[8];
|
||||
short vc_highlight_colors[HI_COLOR_COUNT];
|
||||
int vc_color_pair_end{0};
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "shlex.hh"
|
||||
#include "sql_help.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "static_file_vtab.hh"
|
||||
#include "view_helpers.crumbs.hh"
|
||||
#include "view_helpers.examples.hh"
|
||||
#include "view_helpers.hist.hh"
|
||||
@ -108,6 +109,7 @@ open_schema_view()
|
||||
|
||||
schema += "\n\n-- Virtual Table Definitions --\n\n";
|
||||
schema += ENVIRON_CREATE_STMT;
|
||||
schema += STATIC_FILE_CREATE_STMT;
|
||||
schema += vtab_module_schemas;
|
||||
for (const auto& vtab_iter : *lnav_data.ld_vtab_manager) {
|
||||
schema += "\n" + vtab_iter.second->get_table_statement();
|
||||
|
@ -162,12 +162,14 @@ static const typed_json_path_container<resolved_crumb> breadcrumb_crumb_handlers
|
||||
struct top_line_meta {
|
||||
nonstd::optional<std::string> tlm_time;
|
||||
nonstd::optional<std::string> tlm_file;
|
||||
nonstd::optional<std::string> tlm_anchor;
|
||||
std::vector<resolved_crumb> tlm_crumbs;
|
||||
};
|
||||
|
||||
static const typed_json_path_container<top_line_meta> top_line_meta_handlers = {
|
||||
yajlpp::property_handler("time").for_field(&top_line_meta::tlm_time),
|
||||
yajlpp::property_handler("file").for_field(&top_line_meta::tlm_file),
|
||||
yajlpp::property_handler("anchor").for_field(&top_line_meta::tlm_anchor),
|
||||
yajlpp::property_handler("breadcrumbs#")
|
||||
.for_field(&top_line_meta::tlm_crumbs)
|
||||
.with_children(breadcrumb_crumb_handlers),
|
||||
@ -283,6 +285,7 @@ CREATE TABLE lnav_views (
|
||||
if (tss != nullptr && tss->text_line_count() > 0) {
|
||||
auto* time_source = dynamic_cast<text_time_translator*>(
|
||||
tc.get_sub_source());
|
||||
auto* ta = dynamic_cast<text_anchors*>(tc.get_sub_source());
|
||||
std::vector<breadcrumb::crumb> crumbs;
|
||||
|
||||
tss->text_crumbs_for_line(tc.get_top(), crumbs);
|
||||
@ -302,6 +305,9 @@ CREATE TABLE lnav_views (
|
||||
tlm.tlm_time = timestamp;
|
||||
}
|
||||
}
|
||||
if (ta != nullptr) {
|
||||
tlm.tlm_anchor = ta->anchor_for_row(tc.get_top());
|
||||
}
|
||||
tlm.tlm_file = tc.map_top_row([](const auto& al) {
|
||||
return get_string_attr(al.get_attrs(), logline::L_FILE)
|
||||
| [](const auto wrapper) {
|
||||
@ -385,6 +391,50 @@ CREATE TABLE lnav_views (
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
if (top_meta != nullptr) {
|
||||
static const intern_string_t SQL_SRC
|
||||
= intern_string::lookup("top_meta");
|
||||
|
||||
auto parse_res = top_line_meta_handlers.parser_for(SQL_SRC).of(
|
||||
string_fragment::from_c_str(top_meta));
|
||||
if (parse_res.isErr()) {
|
||||
auto errmsg = parse_res.unwrapErr();
|
||||
tab->zErrMsg = sqlite3_mprintf(
|
||||
"Invalid top_meta: %s",
|
||||
errmsg[0].to_attr_line().get_string().c_str());
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
auto tlm = parse_res.unwrap();
|
||||
|
||||
if (index == LNV_TEXT && tlm.tlm_file) {
|
||||
if (!lnav_data.ld_text_source.to_front(tlm.tlm_file.value())) {
|
||||
auto errmsg = parse_res.unwrapErr();
|
||||
tab->zErrMsg = sqlite3_mprintf("unknown top_meta.file: %s",
|
||||
tlm.tlm_file->c_str());
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
auto* ta = dynamic_cast<text_anchors*>(tc.get_sub_source());
|
||||
if (ta != nullptr && tlm.tlm_anchor
|
||||
&& !tlm.tlm_anchor.value().empty())
|
||||
{
|
||||
auto req_anchor = tlm.tlm_anchor.value();
|
||||
auto req_anchor_top = ta->row_for_anchor(req_anchor);
|
||||
if (req_anchor_top) {
|
||||
auto curr_anchor = ta->anchor_for_row(tc.get_top());
|
||||
|
||||
if (!curr_anchor || curr_anchor.value() != req_anchor) {
|
||||
tc.set_top(req_anchor_top.value());
|
||||
}
|
||||
} else {
|
||||
tab->zErrMsg = sqlite3_mprintf(
|
||||
"unknown top_meta.anchor: %s", req_anchor.c_str());
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
tc.set_left(left);
|
||||
tc.set_paused(is_paused);
|
||||
tc.execute_search(search);
|
||||
|
@ -413,6 +413,8 @@ struct sqlite_func_adapter<Return (*)(Args...), f> {
|
||||
Return retval = f(from_sqlite<Args>()(argc, argv, Idx)...);
|
||||
|
||||
to_sqlite(context, std::move(retval));
|
||||
} catch (const lnav::console::user_message& um) {
|
||||
to_sqlite(context, um);
|
||||
} catch (from_sqlite_conversion_error& e) {
|
||||
char buffer[256];
|
||||
|
||||
@ -935,6 +937,8 @@ struct tvt_iterator_cursor {
|
||||
|
||||
template<typename T>
|
||||
struct tvt_no_update : public T {
|
||||
using T::T;
|
||||
|
||||
int delete_row(sqlite3_vtab* vt, sqlite3_int64 rowid)
|
||||
{
|
||||
vt->zErrMsg = sqlite3_mprintf("Rows cannot be deleted from this table");
|
||||
|
@ -683,7 +683,6 @@ yajlpp_parse_context::update_callbacks(const json_path_container* orig_handlers,
|
||||
}
|
||||
|
||||
this->ypc_sibling_handlers = orig_handlers;
|
||||
|
||||
pcre_input pi(&this->ypc_path[0], 0, this->ypc_path.size() - 1);
|
||||
|
||||
this->ypc_callbacks = DEFAULT_CALLBACKS;
|
||||
@ -731,7 +730,7 @@ yajlpp_parse_context::update_callbacks(const json_path_container* orig_handlers,
|
||||
|| index != yajlpp_provider_context::nindex))
|
||||
{
|
||||
this->ypc_obj_stack.push(jph.jph_obj_provider(
|
||||
{{this->ypc_pcre_context, pi}, index},
|
||||
{{this->ypc_pcre_context, pi}, index, this},
|
||||
this->ypc_obj_stack.top()));
|
||||
}
|
||||
}
|
||||
@ -1080,7 +1079,7 @@ const intern_string_t
|
||||
yajlpp_parse_context::get_full_path() const
|
||||
{
|
||||
if (this->ypc_path.size() <= 1) {
|
||||
static intern_string_t SLASH = intern_string::lookup("/");
|
||||
static const intern_string_t SLASH = intern_string::lookup("/");
|
||||
|
||||
return SLASH;
|
||||
}
|
||||
|
@ -94,6 +94,7 @@ class yajlpp_parse_context;
|
||||
struct yajlpp_provider_context {
|
||||
pcre_extractor ypc_extractor;
|
||||
size_t ypc_index{0};
|
||||
yajlpp_parse_context* ypc_parse_context;
|
||||
|
||||
static constexpr size_t nindex = static_cast<size_t>(-1);
|
||||
|
||||
@ -135,10 +136,7 @@ public:
|
||||
this->ye_msg = reinterpret_cast<const char*>(yajl_msg.in());
|
||||
}
|
||||
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return this->ye_msg.c_str();
|
||||
}
|
||||
const char* what() const noexcept override { return this->ye_msg.c_str(); }
|
||||
|
||||
private:
|
||||
std::string ye_msg;
|
||||
@ -517,10 +515,7 @@ public:
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status operator()()
|
||||
{
|
||||
return yajl_gen_null(this->yg_handle);
|
||||
}
|
||||
yajl_gen_status operator()() { return yajl_gen_null(this->yg_handle); }
|
||||
|
||||
private:
|
||||
yajl_gen yg_handle;
|
||||
@ -543,10 +538,7 @@ public:
|
||||
yajl_gen_map_open(handle);
|
||||
}
|
||||
|
||||
~yajlpp_map()
|
||||
{
|
||||
yajl_gen_map_close(this->ycb_handle);
|
||||
}
|
||||
~yajlpp_map() { yajl_gen_map_close(this->ycb_handle); }
|
||||
};
|
||||
|
||||
class yajlpp_array : public yajlpp_container_base {
|
||||
@ -556,10 +548,7 @@ public:
|
||||
yajl_gen_array_open(handle);
|
||||
}
|
||||
|
||||
~yajlpp_array()
|
||||
{
|
||||
yajl_gen_array_close(this->ycb_handle);
|
||||
}
|
||||
~yajlpp_array() { yajl_gen_array_close(this->ycb_handle); }
|
||||
};
|
||||
|
||||
class yajlpp_gen_context {
|
||||
@ -606,15 +595,9 @@ public:
|
||||
this->yg_handle = yajl_gen_alloc(nullptr);
|
||||
}
|
||||
|
||||
yajl_gen get_handle() const
|
||||
{
|
||||
return this->yg_handle.in();
|
||||
}
|
||||
yajl_gen get_handle() const { return this->yg_handle.in(); }
|
||||
|
||||
operator yajl_gen()
|
||||
{
|
||||
return this->yg_handle.in();
|
||||
}
|
||||
operator yajl_gen() { return this->yg_handle.in(); }
|
||||
|
||||
string_fragment to_string_fragment();
|
||||
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include "config.h"
|
||||
#include "mapbox/variant.hpp"
|
||||
#include "relative_time.hh"
|
||||
#include "view_curses.hh"
|
||||
#include "yajlpp.hh"
|
||||
|
||||
#define FOR_FIELD(T, FIELD) for_field<T, decltype(T ::FIELD), &T ::FIELD>()
|
||||
@ -432,11 +431,13 @@ struct json_path_handler : public json_path_handler_base {
|
||||
}
|
||||
|
||||
if ((rc = yajl_gen_string(handle, pair.first))
|
||||
!= yajl_gen_status_ok) {
|
||||
!= yajl_gen_status_ok)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
if ((rc = yajl_gen_string(handle, pair.second))
|
||||
!= yajl_gen_status_ok) {
|
||||
!= yajl_gen_status_ok)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@ -669,6 +670,22 @@ struct json_path_handler : public json_path_handler_base {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
json_path_handler& for_child(positioned_property<U>(T::*field))
|
||||
{
|
||||
this->jph_obj_provider
|
||||
= [field](const yajlpp_provider_context& ypc, void* root) -> void* {
|
||||
auto& child = json_path_handler::get_field(root, field);
|
||||
|
||||
if (ypc.ypc_parse_context != nullptr && child.pp_path.empty()) {
|
||||
child.pp_path = ypc.ypc_parse_context->get_full_path();
|
||||
}
|
||||
return &child.pp_value;
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
json_path_handler& for_child(Args... args)
|
||||
{
|
||||
@ -685,7 +702,8 @@ struct json_path_handler : public json_path_handler_base {
|
||||
template<typename... Args,
|
||||
std::enable_if_t<
|
||||
LastIs<std::map<std::string, std::string>, Args...>::value,
|
||||
bool> = true>
|
||||
bool>
|
||||
= true>
|
||||
json_path_handler& for_field(Args... args)
|
||||
{
|
||||
this->add_cb(str_field_cb2);
|
||||
@ -732,7 +750,8 @@ struct json_path_handler : public json_path_handler_base {
|
||||
template<typename... Args,
|
||||
std::enable_if_t<
|
||||
LastIs<std::map<std::string, json_any_t>, Args...>::value,
|
||||
bool> = true>
|
||||
bool>
|
||||
= true>
|
||||
json_path_handler& for_field(Args... args)
|
||||
{
|
||||
this->add_cb(bool_field_cb);
|
||||
@ -859,7 +878,8 @@ struct json_path_handler : public json_path_handler_base {
|
||||
template<
|
||||
typename... Args,
|
||||
std::enable_if_t<LastIs<nonstd::optional<std::string>, Args...>::value,
|
||||
bool> = true>
|
||||
bool>
|
||||
= true>
|
||||
json_path_handler& for_field(Args... args)
|
||||
{
|
||||
this->add_cb(str_field_cb2);
|
||||
@ -920,7 +940,8 @@ struct json_path_handler : public json_path_handler_base {
|
||||
template<typename... Args,
|
||||
std::enable_if_t<
|
||||
LastIs<positioned_property<std::string>, Args...>::value,
|
||||
bool> = true>
|
||||
bool>
|
||||
= true>
|
||||
json_path_handler& for_field(Args... args)
|
||||
{
|
||||
this->add_cb(str_field_cb2);
|
||||
@ -975,9 +996,9 @@ struct json_path_handler : public json_path_handler_base {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<
|
||||
typename... Args,
|
||||
std::enable_if_t<LastIs<intern_string_t, Args...>::value, bool> = true>
|
||||
template<typename... Args,
|
||||
std::enable_if_t<LastIs<intern_string_t, Args...>::value, bool>
|
||||
= true>
|
||||
json_path_handler& for_field(Args... args)
|
||||
{
|
||||
this->add_cb(str_field_cb2);
|
||||
@ -1031,7 +1052,8 @@ struct json_path_handler : public json_path_handler_base {
|
||||
template<typename... Args,
|
||||
std::enable_if_t<
|
||||
LastIs<positioned_property<intern_string_t>, Args...>::value,
|
||||
bool> = true>
|
||||
bool>
|
||||
= true>
|
||||
json_path_handler& for_field(Args... args)
|
||||
{
|
||||
this->add_cb(str_field_cb2);
|
||||
@ -1191,9 +1213,10 @@ struct json_path_handler : public json_path_handler_base {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Args,
|
||||
std::enable_if_t<LastIs<std::chrono::seconds, Args...>::value,
|
||||
bool> = true>
|
||||
template<
|
||||
typename... Args,
|
||||
std::enable_if_t<LastIs<std::chrono::seconds, Args...>::value, bool>
|
||||
= true>
|
||||
json_path_handler& for_field(Args... args)
|
||||
{
|
||||
this->add_cb(str_field_cb2);
|
||||
|
103
src/yaml-extension-functions.cc
Normal file
103
src/yaml-extension-functions.cc
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @file yaml-extension-functions.cc
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#define RYML_SINGLE_HDR_DEFINE_NOW
|
||||
|
||||
#include "ryml_all.hpp"
|
||||
#include "sqlite-extension-func.hh"
|
||||
#include "vtab_module.hh"
|
||||
#include "yajlpp/yajlpp.hh"
|
||||
|
||||
using namespace lnav::roles::literals;
|
||||
|
||||
static void
|
||||
ryml_error_to_um(const char* msg, size_t len, ryml::Location loc, void* ud)
|
||||
{
|
||||
intern_string_t src = intern_string::lookup(
|
||||
string_fragment::from_bytes(loc.name.data(), loc.name.size()));
|
||||
auto& sf = *(static_cast<string_fragment*>(ud));
|
||||
auto msg_str = string_fragment::from_bytes(msg, len).trim().to_string();
|
||||
|
||||
if (loc.offset == sf.length()) {
|
||||
loc.line -= 1;
|
||||
}
|
||||
throw lnav::console::user_message::error("failed to parse YAML content")
|
||||
.with_reason(msg_str)
|
||||
.with_snippet(lnav::console::snippet::from(
|
||||
source_location{src, (int32_t) loc.line}, ""));
|
||||
}
|
||||
|
||||
static text_auto_buffer
|
||||
yaml_to_json(string_fragment in)
|
||||
{
|
||||
ryml::Callbacks callbacks(&in, nullptr, nullptr, ryml_error_to_um);
|
||||
|
||||
ryml::set_callbacks(callbacks);
|
||||
auto tree = ryml::parse_in_arena(
|
||||
"input", ryml::csubstr{in.data(), (size_t) in.length()});
|
||||
|
||||
auto output = ryml::emit_json(
|
||||
tree, tree.root_id(), ryml::substr{}, /*error_on_excess*/ false);
|
||||
auto buf = auto_buffer::alloc(output.len);
|
||||
buf.resize(output.len);
|
||||
output = ryml::emit_json(tree,
|
||||
tree.root_id(),
|
||||
ryml::substr(buf.in(), buf.size()),
|
||||
/*error_on_excess*/ true);
|
||||
|
||||
return {std::move(buf)};
|
||||
}
|
||||
|
||||
int
|
||||
yaml_extension_functions(struct FuncDef** basic_funcs,
|
||||
struct FuncDefAgg** agg_funcs)
|
||||
{
|
||||
static struct FuncDef yaml_funcs[] = {
|
||||
sqlite_func_adapter<decltype(&yaml_to_json), yaml_to_json>::builder(
|
||||
help_text("yaml_to_json",
|
||||
"Convert a YAML document to a JSON-encoded string")
|
||||
.sql_function()
|
||||
.with_parameter({"yaml", "The YAML value to convert to JSON."})
|
||||
.with_tags({"json", "yaml"})
|
||||
.with_example({
|
||||
"To convert the document \"abc: def\"",
|
||||
"SELECT yaml_to_json('abc: def')",
|
||||
})),
|
||||
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
*basic_funcs = yaml_funcs;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
@ -208,6 +208,7 @@ dist_noinst_SCRIPTS = \
|
||||
test_sql_time_func.sh \
|
||||
test_sql_views_vtab.sh \
|
||||
test_sql_xml_func.sh \
|
||||
test_sql_yaml_func.sh \
|
||||
test_text_file.sh \
|
||||
test_tui.sh \
|
||||
test_view_colors.sh \
|
||||
@ -411,6 +412,7 @@ TESTS = \
|
||||
test_sql_time_func.sh \
|
||||
test_sql_views_vtab.sh \
|
||||
test_sql_xml_func.sh \
|
||||
test_sql_yaml_func.sh \
|
||||
test_text_file.sh \
|
||||
test_tui.sh \
|
||||
test_data_parser.sh \
|
||||
|
@ -118,6 +118,9 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
Lorem ipsum
|
||||
|
||||
AbcDef
|
||||
Lorem ipsum
|
||||
|
||||
)";
|
||||
|
||||
auto meta = lnav::document::discover_structure(INPUT, line_range{0, -1});
|
||||
|
@ -302,6 +302,8 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_meta.sh_41f643bb4f720130625b042563e9591bee4ae588.out \
|
||||
$(srcdir)/%reldir%/test_meta.sh_45ff39a3d0ac0ca0c95aaca14d043450cec1cedd.err \
|
||||
$(srcdir)/%reldir%/test_meta.sh_45ff39a3d0ac0ca0c95aaca14d043450cec1cedd.out \
|
||||
$(srcdir)/%reldir%/test_meta.sh_48e85ba0c0945a5085fb4ee255771406061a9c17.err \
|
||||
$(srcdir)/%reldir%/test_meta.sh_48e85ba0c0945a5085fb4ee255771406061a9c17.out \
|
||||
$(srcdir)/%reldir%/test_meta.sh_4c39b356748c67ccf8a6027a1af88da532f8252a.err \
|
||||
$(srcdir)/%reldir%/test_meta.sh_4c39b356748c67ccf8a6027a1af88da532f8252a.out \
|
||||
$(srcdir)/%reldir%/test_meta.sh_7b75763926d832bf9784ca234a060859770aabe7.err \
|
||||
@ -912,6 +914,8 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_sql_time_func.sh_f3b1ea49779117bf45f85ad5615fdc5e89193db6.out \
|
||||
$(srcdir)/%reldir%/test_sql_views_vtab.sh_28e23f4e98b1acd6478e39844fd9306b444550c3.err \
|
||||
$(srcdir)/%reldir%/test_sql_views_vtab.sh_28e23f4e98b1acd6478e39844fd9306b444550c3.out \
|
||||
$(srcdir)/%reldir%/test_sql_views_vtab.sh_32acc1a8bb5028636fdbf08f077f9a835ab51bec.err \
|
||||
$(srcdir)/%reldir%/test_sql_views_vtab.sh_32acc1a8bb5028636fdbf08f077f9a835ab51bec.out \
|
||||
$(srcdir)/%reldir%/test_sql_views_vtab.sh_485a6ac7c69bd4b5d34d3399a9c17f6a2dc89ad3.err \
|
||||
$(srcdir)/%reldir%/test_sql_views_vtab.sh_485a6ac7c69bd4b5d34d3399a9c17f6a2dc89ad3.out \
|
||||
$(srcdir)/%reldir%/test_sql_views_vtab.sh_62d15cb9d5a9259f198aa01ca8ed200d6da38d68.err \
|
||||
@ -960,12 +964,22 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_sql_xml_func.sh_b036c73528a446cba46625767517cdac868aba72.out \
|
||||
$(srcdir)/%reldir%/test_sql_xml_func.sh_fefeb387ae14d4171225ea06cbbff3ec43990cf0.err \
|
||||
$(srcdir)/%reldir%/test_sql_xml_func.sh_fefeb387ae14d4171225ea06cbbff3ec43990cf0.out \
|
||||
$(srcdir)/%reldir%/test_sql_yaml_func.sh_41c6abde708a69e74f5b7fde865d88fa75f91e0a.err \
|
||||
$(srcdir)/%reldir%/test_sql_yaml_func.sh_41c6abde708a69e74f5b7fde865d88fa75f91e0a.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_2e69c22dcfa37b5c3e8490a6026eacb7ca953998.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_2e69c22dcfa37b5c3e8490a6026eacb7ca953998.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_6a24078983cf1b7a80b6fb65d5186cd125498136.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_6a24078983cf1b7a80b6fb65d5186cd125498136.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_87943c6be50d701a03e901f16493314c839af1ab.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_87943c6be50d701a03e901f16493314c839af1ab.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_ac486314c4e02e480d829ea2f077b86c49fedcec.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_ac486314c4e02e480d829ea2f077b86c49fedcec.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_ac872aadda29b9a824361a2c711d62ec1c75d40f.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_ac872aadda29b9a824361a2c711d62ec1c75d40f.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_c2a346ca1da2da4346f1d310212e166767993ce9.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_c2a346ca1da2da4346f1d310212e166767993ce9.out \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_e088ea61a5382458cc48a2607e2639e52b0be1da.err \
|
||||
$(srcdir)/%reldir%/test_text_file.sh_e088ea61a5382458cc48a2607e2639e52b0be1da.out \
|
||||
$()
|
||||
|
@ -389,7 +389,7 @@ can always use [37m[40m q [0m to pop the top view off of the stack.
|
||||
|
||||
Key(s) Action
|
||||
════════════════════════════════════════════════════════════════════════
|
||||
[1m/[0mregexp Start a search for the given regular expression.
|
||||
[1m/[0m[4mregexp[0m Start a search for the given regular expression.
|
||||
The search is live, so when there is a pause in
|
||||
typing, the currently running search will be
|
||||
canceled and a new one started. The first ten
|
||||
@ -770,7 +770,9 @@ For support questions, email:
|
||||
|
||||
[4m:[0m[1m[4mcomment[0m[4m [0m[4mtext[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Attach a comment to the top log line
|
||||
Attach a comment to the top log line. The comment will be displayed
|
||||
right below the log message it is associated with. The comment can
|
||||
be formatted using markdown and you can add new-lines with '\n'.
|
||||
[4mParameter[0m
|
||||
[4mtext[0m The comment text
|
||||
[4mSee Also[0m
|
||||
@ -1045,12 +1047,13 @@ For support questions, email:
|
||||
|
||||
|
||||
|
||||
[4m:[0m[1m[4mgoto[0m[4m [0m[4mline#|N%|timestamp[0m
|
||||
[4m:[0m[1m[4mgoto[0m[4m [0m[4mline#|N%|timestamp|#anchor[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Go to the given location in the top view
|
||||
[4mParameter[0m
|
||||
[4mline#|N%|timestamp[0m A line number, percent
|
||||
into the file, or a timestamp
|
||||
[4mline#|N%|timestamp|#anchor[0m A line
|
||||
number, percent into the file,
|
||||
timestamp, or an anchor in a text file
|
||||
[4mSee Also[0m
|
||||
[1m:next-location[0m, [1m:next-mark[0m, [1m:prev-location[0m, [1m:prev-mark[0m, [1m:relative-goto[0m
|
||||
[4mExamples[0m
|
||||
@ -1066,6 +1069,10 @@ For support questions, email:
|
||||
[37m[40m:[0m[1m[36m[40mgoto[0m[37m[40m 2017-01-01 [0m
|
||||
|
||||
|
||||
#4 To go to the Screenshots section:
|
||||
[37m[40m:[0m[1m[36m[40mgoto[0m[37m[40m #screenshots [0m
|
||||
|
||||
|
||||
|
||||
[4m:[0m[1m[4mhelp[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
@ -2600,7 +2607,7 @@ For support questions, email:
|
||||
[4mdefault[0m The default value if the value was not found
|
||||
[4mSee Also[0m
|
||||
[1mjson_concat()[0m, [1mjson_contains()[0m, [1mjson_group_array()[0m,
|
||||
[1mjson_group_object()[0m
|
||||
[1mjson_group_object()[0m, [1myaml_to_json()[0m
|
||||
[4mExamples[0m
|
||||
#1 To get the root of a JSON value:
|
||||
[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40mjget[0m[37m[40m([0m[35m[40m'1'[0m[37m[40m, [0m[35m[40m''[0m[37m[40m) [0m
|
||||
@ -2655,7 +2662,8 @@ For support questions, email:
|
||||
[4mjson[0m The initial JSON value.
|
||||
[4mvalue[0m The value(s) to add to the end of the array.
|
||||
[4mSee Also[0m
|
||||
[1mjget()[0m, [1mjson_contains()[0m, [1mjson_group_array()[0m, [1mjson_group_object()[0m
|
||||
[1mjget()[0m, [1mjson_contains()[0m, [1mjson_group_array()[0m, [1mjson_group_object()[0m,
|
||||
[1myaml_to_json()[0m
|
||||
[4mExamples[0m
|
||||
#1 To append the number 4 to null:
|
||||
[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40mjson_concat[0m[37m[40m([0m[1m[36m[40mNULL[0m[37m[40m, [0m[1m[37m[40m4[0m[37m[40m) [0m
|
||||
@ -2677,7 +2685,8 @@ For support questions, email:
|
||||
[4mjson[0m The JSON value to query.
|
||||
[4mvalue[0m The value to look for in the first argument
|
||||
[4mSee Also[0m
|
||||
[1mjget()[0m, [1mjson_concat()[0m, [1mjson_group_array()[0m, [1mjson_group_object()[0m
|
||||
[1mjget()[0m, [1mjson_concat()[0m, [1mjson_group_array()[0m, [1mjson_group_object()[0m,
|
||||
[1myaml_to_json()[0m
|
||||
[4mExamples[0m
|
||||
#1 To test if a JSON array contains the number 4:
|
||||
[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40mjson_contains[0m[37m[40m([0m[35m[40m'[1, 2, 3]'[0m[37m[40m, [0m[1m[37m[40m4[0m[37m[40m) [0m
|
||||
@ -2694,7 +2703,8 @@ For support questions, email:
|
||||
[4mParameter[0m
|
||||
[4mvalue[0m The values to append to the array
|
||||
[4mSee Also[0m
|
||||
[1mjget()[0m, [1mjson_concat()[0m, [1mjson_contains()[0m, [1mjson_group_object()[0m
|
||||
[1mjget()[0m, [1mjson_concat()[0m, [1mjson_contains()[0m, [1mjson_group_object()[0m,
|
||||
[1myaml_to_json()[0m
|
||||
[4mExamples[0m
|
||||
#1 To create an array from arguments:
|
||||
[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40mjson_group_array[0m[37m[40m([0m[35m[40m'one'[0m[37m[40m, [0m[1m[37m[40m2[0m[37m[40m, [0m[1m[37m[40m3.4[0m[37m[40m) [0m
|
||||
@ -2712,7 +2722,8 @@ For support questions, email:
|
||||
[4mname[0m The property name for the value
|
||||
[4mvalue[0m The value to add to the object
|
||||
[4mSee Also[0m
|
||||
[1mjget()[0m, [1mjson_concat()[0m, [1mjson_contains()[0m, [1mjson_group_array()[0m
|
||||
[1mjget()[0m, [1mjson_concat()[0m, [1mjson_contains()[0m, [1mjson_group_array()[0m,
|
||||
[1myaml_to_json()[0m
|
||||
[4mExamples[0m
|
||||
#1 To create an object from arguments:
|
||||
[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40mjson_group_object[0m[37m[40m([0m[35m[40m'a'[0m[37m[40m, [0m[1m[37m[40m1[0m[37m[40m, [0m[35m[40m'b'[0m[37m[40m, [0m[1m[37m[40m2[0m[37m[40m) [0m
|
||||
@ -4122,6 +4133,20 @@ For support questions, email:
|
||||
|
||||
|
||||
|
||||
[1m[4myaml_to_json[0m[4m([0m[4myaml[0m[4m)[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Convert a YAML document to a JSON-encoded string
|
||||
[4mParameter[0m
|
||||
[4myaml[0m The YAML value to convert to JSON.
|
||||
[4mSee Also[0m
|
||||
[1mjget()[0m, [1mjson_concat()[0m, [1mjson_contains()[0m, [1mjson_group_array()[0m,
|
||||
[1mjson_group_object()[0m
|
||||
[4mExample[0m
|
||||
#1 To convert the document "abc: def":
|
||||
[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40myaml_to_json[0m[37m[40m([0m[35m[40m'abc: def'[0m[37m[40m) [0m
|
||||
|
||||
|
||||
|
||||
[1m[4mzeroblob[0m[4m([0m[4mN[0m[4m)[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Returns a BLOB consisting of N bytes of 0x00.
|
||||
|
@ -2,6 +2,6 @@
|
||||
[1m[31mreason[0m: expecting line number/percentage, timestamp, or relative time
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m:[0m[1m[36m[40mgoto[0m[37m[40m invalid [0m
|
||||
[36m =[0m [36mhelp[0m: [4m:[0m[1m[4mgoto[0m[4m [0m[4mline#|N%|timestamp[0m
|
||||
[36m =[0m [36mhelp[0m: [4m:[0m[1m[4mgoto[0m[4m [0m[4mline#|N%|timestamp|#anchor[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Go to the given location in the top view
|
||||
|
@ -0,0 +1,6 @@
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
│ [32mHello, [0m[1m[32mWorld[0m[32m![0m
|
||||
│
|
||||
└ [32mThis is [0m[32m[40m markdown [0m[32m now![0m
|
||||
[31m192.168.202.254[0m[31m - [0m[31m-[0m[31m [[0m[31m20/Jul/2009:22:59:29 +0000[0m[31m] "[0m[31mGET[0m[31m [0m[31m/vmw/vSphere/default/vmkboot.gz[0m[31m [0m[31mHTTP/1.0[0m[31m" 404 46210 "[0m[31m-[0m[31m" "[0m[31mgPXE/0.9.7[0m[31m"[0m
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
|
@ -0,0 +1,19 @@
|
||||
[4mBuild[0m
|
||||
|
||||
Lnav follows the usual GNU style for configuring and installing
|
||||
software:
|
||||
|
||||
Run [37m[40m ./autogen.sh [0m if compiling from a cloned repository.
|
||||
|
||||
▌[33m[40m$ [0m[37m[40m./configure [0m
|
||||
▌[33m[40m$ [0m[37m[40mmake [0m
|
||||
▌[33m[40m$ [0m[37m[40msudo make install [0m
|
||||
|
||||
[1mSee Also[0m
|
||||
|
||||
[4mAngle-grinder[1][0m is a tool to slice and dice log files on the
|
||||
command-line. If you're familiar with the SumoLogic query language,
|
||||
you might find this tool more comfortable to work with.
|
||||
|
||||
▌[1] - https://github.com/rcoh/angle-grinder
|
||||
|
@ -0,0 +1,4 @@
|
||||
[1m[31m✘ error[0m: failed to parse YAML content
|
||||
[1m[31mreason[0m: closing ] not found
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40myaml_to_json[0m[37m[40m([0m[35m[40m'[abc'[0m[37m[40m) [0m
|
@ -0,0 +1,2 @@
|
||||
[1m[31m✘ error[0m: unable to open file: [1mnon-existent:[0m
|
||||
[1m[31mreason[0m: failed to ssh to host: ssh: Could not resolve hostname non-existent: nodename nor servname provided, or not known
|
@ -6,17 +6,17 @@
|
||||
▌[4] - https://docs.lnav.org
|
||||
▌[5] - https://coveralls.io/repos/github/tstack/lnav/badge.svg?[4mbranch[0m=master
|
||||
▌[6] - https://coveralls.io/github/tstack/lnav?[4mbranch[0m=master
|
||||
▌[7] - https://snapcraft.io//lnav/badge.svg
|
||||
▌[7] - https://snapcraft.io/lnav/badge.svg
|
||||
▌[8] - https://snapcraft.io/lnav
|
||||
|
||||
[4m<img[0m
|
||||
[4msrc[0m[4m=[0m[4m[35m"https://assets-global.website-files.com/6257adef93867e50d84d30e2/62594fddd654fc29fcc07359_cb48d2a8d4991281d7a6a95d2f58195e.svg"[0m
|
||||
[4mheight[0m[4m=[0m[4m[35m"20"[0m[4m>[1][0m
|
||||
[4mheight[0m[4m=[0m[4m[35m"20"[0m[4m/>[1][0m
|
||||
|
||||
▌[1] - https://discord.gg/erBPnKwz7R
|
||||
|
||||
This is the source repository for [1mlnav[0m, visit [4mhttps://lnav.org[1][0m for
|
||||
a high level overview.
|
||||
[4mThis is the source repository for [0m[1m[4mlnav[0m[4m, visit [0m[4mhttps://lnav.org[1][0m[4m for[0m
|
||||
[4ma high level overview.[0m
|
||||
|
||||
▌[1] - https://lnav.org
|
||||
|
||||
|
@ -0,0 +1,149 @@
|
||||
[1mScreenshot[0m
|
||||
|
||||
The following screenshot shows a syslog file. Log lines are displayed
|
||||
with highlights. Errors are red and warnings are yellow.
|
||||
|
||||
[4mScreenshot[1][0m[4m[2][0m
|
||||
|
||||
▌[1] - file://{top_srcdir}/docs/assets/images/lnav-syslog-thumb.png
|
||||
▌[2] - file://{top_srcdir}/docs/assets/images/lnav-syslog.png
|
||||
|
||||
[1mFeatures[0m
|
||||
|
||||
[33m•[0m Log messages from different files are collated together
|
||||
into a single view
|
||||
[33m•[0m Automatic detection of log format
|
||||
[33m•[0m Automatic decompression of GZip and BZip2 files
|
||||
[33m•[0m Filter log messages based on regular expressions
|
||||
[33m•[0m Use SQL to analyze your logs
|
||||
[33m•[0m And more...
|
||||
|
||||
[1mInstallation[0m
|
||||
|
||||
[4mDownload a statically-linked binary for Linux/MacOS from the release[0m
|
||||
[4mpage[1][0m
|
||||
|
||||
▌[1] - https://github.com/tstack/lnav/releases/latest#release-artifacts
|
||||
|
||||
[1mUsage[0m
|
||||
|
||||
The only file installed is the executable, [37m[40m lnav [0m. You can execute it
|
||||
with no arguments to view the default set of files:
|
||||
|
||||
▌[37m[40m$ lnav [0m
|
||||
|
||||
You can view all the syslog messages by running:
|
||||
|
||||
▌[37m[40m$ lnav /var/log/messages* [0m
|
||||
|
||||
[4mUsage with [0m[4m[37m[40m systemd-journald [0m
|
||||
|
||||
On systems running [37m[40m systemd-journald [0m, you can use [37m[40m lnav [0m as the
|
||||
pager:
|
||||
|
||||
▌[37m[40m$ journalctl | lnav [0m
|
||||
|
||||
or in follow mode:
|
||||
|
||||
▌[37m[40m$ journalctl -f | lnav [0m
|
||||
|
||||
Since [37m[40m journalctl [0m's default output format omits the year, if you are
|
||||
viewing logs which span multiple years you will need to change the
|
||||
output format to include the year, otherwise [37m[40m lnav [0m gets confused:
|
||||
|
||||
▌[37m[40m$ journalctl -o short-iso | lnav [0m
|
||||
|
||||
It is also possible to use [37m[40m journalctl [0m's json output format and [37m[40m lnav[0m
|
||||
will make use of additional fields such as PRIORITY and _SYSTEMD_UNIT:
|
||||
|
||||
▌[37m[40m$ journalctl -o json | lnav [0m
|
||||
|
||||
In case some MESSAGE fields contain special characters such as ANSI
|
||||
color codes which are considered as unprintable by journalctl,
|
||||
specifying [37m[40m journalctl [0m's [37m[40m -a [0m option might be preferable in order to
|
||||
output those messages still in a non-binary representation:
|
||||
|
||||
▌[37m[40m$ journalctl -a -o json | lnav [0m
|
||||
|
||||
If using systemd v236 or newer, the output fields can be limited to
|
||||
the ones actually recognized by [37m[40m lnav [0m for increased efficiency:
|
||||
|
||||
▌[37m[40m$ journalctl -o json [0m[4m[37m[40m--output-fields[0m[37m[40m=MESSAGE,PRIORITY,_PID,SYSLOG_IDENTIFIER,_SYSTEMD_UNIT | lnav [0m
|
||||
|
||||
If your system has been running for a long time, for increased
|
||||
efficiency you may want to limit the number of log lines fed into [37m[40m lnav[0m
|
||||
, e.g. via [37m[40m journalctl [0m's [37m[40m -n [0m or [37m[40m [0m[4m[37m[40m--since[0m[37m[40m=... [0m options.
|
||||
|
||||
In case of a persistent journal, you may want to limit the number of
|
||||
log lines fed into [37m[40m lnav [0m via [37m[40m journalctl [0m's [37m[40m -b [0m option.
|
||||
|
||||
[1mSupport[0m
|
||||
|
||||
Please file issues on this repository or use the discussions section.
|
||||
The following alternatives are also available:
|
||||
|
||||
[33m•[0m [4msupport@lnav.org[1][0m
|
||||
[33m•[0m [4mDiscord[2][0m
|
||||
[33m•[0m [4mGoogle Groups[3][0m
|
||||
|
||||
▌[1] - mailto:support@lnav.org
|
||||
▌[2] - https://discord.gg/erBPnKwz7R
|
||||
▌[3] - https://groups.google.com/g/lnav
|
||||
|
||||
[1mLinks[0m
|
||||
|
||||
[33m•[0m [4mMain Site[1][0m
|
||||
[33m•[0m [1m[4mDocumentation[0m[4m[2][0m on Read the Docs
|
||||
[33m•[0m [4mInternal Architecture[3][0m
|
||||
|
||||
▌[1] - https://lnav.org
|
||||
▌[2] - https://docs.lnav.org
|
||||
▌[3] - file://{top_srcdir}/ARCHITECTURE.md
|
||||
|
||||
[1mContributing[0m
|
||||
|
||||
[33m•[0m [4mBecome a Sponsor on GitHub[1][0m
|
||||
|
||||
▌[1] - https://github.com/sponsors/tstack
|
||||
|
||||
[4mBuilding From Source[0m
|
||||
|
||||
[4mPrerequisites[0m
|
||||
|
||||
The following software packages are required to build lnav:
|
||||
|
||||
[33m•[0m gcc/clang - A C++14-compatible compiler.
|
||||
[33m•[0m libpcre - The Perl Compatible Regular Expression
|
||||
(PCRE) library.
|
||||
[33m•[0m sqlite - The SQLite database engine. Version 3.9.0
|
||||
or higher is required.
|
||||
[33m•[0m ncurses - The ncurses text UI library.
|
||||
[33m•[0m readline - The readline line editing library.
|
||||
[33m•[0m zlib - The zlib compression library.
|
||||
[33m•[0m bz2 - The bzip2 compression library.
|
||||
[33m•[0m libcurl - The cURL library for downloading files
|
||||
from URLs. Version 7.23.0 or higher is required.
|
||||
[33m•[0m libarchive - The libarchive library for opening archive
|
||||
files, like zip/tgz.
|
||||
[33m•[0m wireshark - The [35m'tshark'[0m program is used to interpret
|
||||
pcap files.
|
||||
|
||||
[4mBuild[0m
|
||||
|
||||
Lnav follows the usual GNU style for configuring and installing
|
||||
software:
|
||||
|
||||
Run [37m[40m ./autogen.sh [0m if compiling from a cloned repository.
|
||||
|
||||
▌[33m[40m$ [0m[37m[40m./configure [0m
|
||||
▌[33m[40m$ [0m[37m[40mmake [0m
|
||||
▌[33m[40m$ [0m[37m[40msudo make install [0m
|
||||
|
||||
[1mSee Also[0m
|
||||
|
||||
[4mAngle-grinder[1][0m is a tool to slice and dice log files on the
|
||||
command-line. If you're familiar with the SumoLogic query language,
|
||||
you might find this tool more comfortable to work with.
|
||||
|
||||
▌[1] - https://github.com/rcoh/angle-grinder
|
||||
|
@ -0,0 +1,4 @@
|
||||
you might find this tool more comfortable to work with.
|
||||
|
||||
▌[1] - https://github.com/rcoh/angle-grinder
|
||||
|
@ -2,6 +2,7 @@
|
||||
{
|
||||
"top_meta": {
|
||||
"file": "{top_srcdir}/README.md",
|
||||
"anchor": "#support",
|
||||
"breadcrumbs": [
|
||||
{
|
||||
"display_value": "README.md",
|
||||
|
@ -0,0 +1,149 @@
|
||||
[1mScreenshot[0m
|
||||
|
||||
The following screenshot shows a syslog file. Log lines are displayed
|
||||
with highlights. Errors are red and warnings are yellow.
|
||||
|
||||
[4mScreenshot[1][0m[4m[2][0m
|
||||
|
||||
▌[1] - file://{top_srcdir}/docs/assets/images/lnav-syslog-thumb.png
|
||||
▌[2] - file://{top_srcdir}/docs/assets/images/lnav-syslog.png
|
||||
|
||||
[1mFeatures[0m
|
||||
|
||||
[33m•[0m Log messages from different files are collated together
|
||||
into a single view
|
||||
[33m•[0m Automatic detection of log format
|
||||
[33m•[0m Automatic decompression of GZip and BZip2 files
|
||||
[33m•[0m Filter log messages based on regular expressions
|
||||
[33m•[0m Use SQL to analyze your logs
|
||||
[33m•[0m And more...
|
||||
|
||||
[1mInstallation[0m
|
||||
|
||||
[4mDownload a statically-linked binary for Linux/MacOS from the release[0m
|
||||
[4mpage[1][0m
|
||||
|
||||
▌[1] - https://github.com/tstack/lnav/releases/latest#release-artifacts
|
||||
|
||||
[1mUsage[0m
|
||||
|
||||
The only file installed is the executable, [37m[40m lnav [0m. You can execute it
|
||||
with no arguments to view the default set of files:
|
||||
|
||||
▌[37m[40m$ lnav [0m
|
||||
|
||||
You can view all the syslog messages by running:
|
||||
|
||||
▌[37m[40m$ lnav /var/log/messages* [0m
|
||||
|
||||
[4mUsage with [0m[4m[37m[40m systemd-journald [0m
|
||||
|
||||
On systems running [37m[40m systemd-journald [0m, you can use [37m[40m lnav [0m as the
|
||||
pager:
|
||||
|
||||
▌[37m[40m$ journalctl | lnav [0m
|
||||
|
||||
or in follow mode:
|
||||
|
||||
▌[37m[40m$ journalctl -f | lnav [0m
|
||||
|
||||
Since [37m[40m journalctl [0m's default output format omits the year, if you are
|
||||
viewing logs which span multiple years you will need to change the
|
||||
output format to include the year, otherwise [37m[40m lnav [0m gets confused:
|
||||
|
||||
▌[37m[40m$ journalctl -o short-iso | lnav [0m
|
||||
|
||||
It is also possible to use [37m[40m journalctl [0m's json output format and [37m[40m lnav[0m
|
||||
will make use of additional fields such as PRIORITY and _SYSTEMD_UNIT:
|
||||
|
||||
▌[37m[40m$ journalctl -o json | lnav [0m
|
||||
|
||||
In case some MESSAGE fields contain special characters such as ANSI
|
||||
color codes which are considered as unprintable by journalctl,
|
||||
specifying [37m[40m journalctl [0m's [37m[40m -a [0m option might be preferable in order to
|
||||
output those messages still in a non-binary representation:
|
||||
|
||||
▌[37m[40m$ journalctl -a -o json | lnav [0m
|
||||
|
||||
If using systemd v236 or newer, the output fields can be limited to
|
||||
the ones actually recognized by [37m[40m lnav [0m for increased efficiency:
|
||||
|
||||
▌[37m[40m$ journalctl -o json [0m[4m[37m[40m--output-fields[0m[37m[40m=MESSAGE,PRIORITY,_PID,SYSLOG_IDENTIFIER,_SYSTEMD_UNIT | lnav [0m
|
||||
|
||||
If your system has been running for a long time, for increased
|
||||
efficiency you may want to limit the number of log lines fed into [37m[40m lnav[0m
|
||||
, e.g. via [37m[40m journalctl [0m's [37m[40m -n [0m or [37m[40m [0m[4m[37m[40m--since[0m[37m[40m=... [0m options.
|
||||
|
||||
In case of a persistent journal, you may want to limit the number of
|
||||
log lines fed into [37m[40m lnav [0m via [37m[40m journalctl [0m's [37m[40m -b [0m option.
|
||||
|
||||
[1mSupport[0m
|
||||
|
||||
Please file issues on this repository or use the discussions section.
|
||||
The following alternatives are also available:
|
||||
|
||||
[33m•[0m [4msupport@lnav.org[1][0m
|
||||
[33m•[0m [4mDiscord[2][0m
|
||||
[33m•[0m [4mGoogle Groups[3][0m
|
||||
|
||||
▌[1] - mailto:support@lnav.org
|
||||
▌[2] - https://discord.gg/erBPnKwz7R
|
||||
▌[3] - https://groups.google.com/g/lnav
|
||||
|
||||
[1mLinks[0m
|
||||
|
||||
[33m•[0m [4mMain Site[1][0m
|
||||
[33m•[0m [1m[4mDocumentation[0m[4m[2][0m on Read the Docs
|
||||
[33m•[0m [4mInternal Architecture[3][0m
|
||||
|
||||
▌[1] - https://lnav.org
|
||||
▌[2] - https://docs.lnav.org
|
||||
▌[3] - file://{top_srcdir}/ARCHITECTURE.md
|
||||
|
||||
[1mContributing[0m
|
||||
|
||||
[33m•[0m [4mBecome a Sponsor on GitHub[1][0m
|
||||
|
||||
▌[1] - https://github.com/sponsors/tstack
|
||||
|
||||
[4mBuilding From Source[0m
|
||||
|
||||
[4mPrerequisites[0m
|
||||
|
||||
The following software packages are required to build lnav:
|
||||
|
||||
[33m•[0m gcc/clang - A C++14-compatible compiler.
|
||||
[33m•[0m libpcre - The Perl Compatible Regular Expression
|
||||
(PCRE) library.
|
||||
[33m•[0m sqlite - The SQLite database engine. Version 3.9.0
|
||||
or higher is required.
|
||||
[33m•[0m ncurses - The ncurses text UI library.
|
||||
[33m•[0m readline - The readline line editing library.
|
||||
[33m•[0m zlib - The zlib compression library.
|
||||
[33m•[0m bz2 - The bzip2 compression library.
|
||||
[33m•[0m libcurl - The cURL library for downloading files
|
||||
from URLs. Version 7.23.0 or higher is required.
|
||||
[33m•[0m libarchive - The libarchive library for opening archive
|
||||
files, like zip/tgz.
|
||||
[33m•[0m wireshark - The [35m'tshark'[0m program is used to interpret
|
||||
pcap files.
|
||||
|
||||
[4mBuild[0m
|
||||
|
||||
Lnav follows the usual GNU style for configuring and installing
|
||||
software:
|
||||
|
||||
Run [37m[40m ./autogen.sh [0m if compiling from a cloned repository.
|
||||
|
||||
▌[33m[40m$ [0m[37m[40m./configure [0m
|
||||
▌[33m[40m$ [0m[37m[40mmake [0m
|
||||
▌[33m[40m$ [0m[37m[40msudo make install [0m
|
||||
|
||||
[1mSee Also[0m
|
||||
|
||||
[4mAngle-grinder[1][0m is a tool to slice and dice log files on the
|
||||
command-line. If you're familiar with the SumoLogic query language,
|
||||
you might find this tool more comfortable to work with.
|
||||
|
||||
▌[1] - https://github.com/rcoh/angle-grinder
|
||||
|
@ -101,3 +101,10 @@ run_cap_test ${lnav_test} -n \
|
||||
run_cap_test ${lnav_test} -d /tmp/lnav.err -n \
|
||||
-I ${test_dir} \
|
||||
${test_dir}/logfile_xml_msg.0
|
||||
|
||||
run_cap_test ${lnav_test} -n -f- \
|
||||
${test_dir}/logfile_access_log.0 <<'EOF'
|
||||
:comment Hello, **World**!
|
||||
|
||||
This is `markdown` now!
|
||||
EOF
|
||||
|
@ -727,7 +727,7 @@ EOF
|
||||
|
||||
|
||||
schema_dump() {
|
||||
${lnav_test} -n -c ';.schema' ${test_dir}/logfile_access_log.0 | head -n19
|
||||
${lnav_test} -n -c ';.schema' ${test_dir}/logfile_access_log.0 | head -n21
|
||||
}
|
||||
|
||||
run_test schema_dump
|
||||
@ -735,12 +735,14 @@ run_test schema_dump
|
||||
check_output "schema view is not working" <<EOF
|
||||
ATTACH DATABASE '' AS 'main';
|
||||
CREATE VIRTUAL TABLE environ USING environ_vtab_impl();
|
||||
CREATE VIRTUAL TABLE lnav_static_files USING lnav_static_file_vtab_impl();
|
||||
CREATE VIRTUAL TABLE lnav_views USING lnav_views_impl();
|
||||
CREATE VIRTUAL TABLE lnav_view_filter_stats USING lnav_view_filter_stats_impl();
|
||||
CREATE VIRTUAL TABLE lnav_view_files USING lnav_view_files_impl();
|
||||
CREATE VIRTUAL TABLE lnav_view_stack USING lnav_view_stack_impl();
|
||||
CREATE VIRTUAL TABLE lnav_view_filters USING lnav_view_filters_impl();
|
||||
CREATE VIRTUAL TABLE lnav_file USING lnav_file_impl();
|
||||
CREATE VIRTUAL TABLE lnav_file_metadata USING lnav_file_metadata_impl();
|
||||
CREATE VIEW lnav_view_filters_and_stats AS
|
||||
SELECT * FROM lnav_view_filters LEFT NATURAL JOIN lnav_view_filter_stats;
|
||||
CREATE VIRTUAL TABLE regexp_capture USING regexp_capture_impl();
|
||||
|
@ -167,3 +167,7 @@ run_cap_test ${lnav_test} -n \
|
||||
-c ":goto 0" \
|
||||
-c ":next-mark search" \
|
||||
${test_dir}/logfile_generic.0
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ";UPDATE lnav_views SET top_meta = json_object('anchor', '#build') WHERE name = 'text'" \
|
||||
${top_srcdir}/README.md
|
||||
|
5
test/test_sql_yaml_func.sh
Normal file
5
test/test_sql_yaml_func.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#! /bin/bash
|
||||
|
||||
export YES_COLOR=1
|
||||
|
||||
run_cap_test ${lnav_test} -n -c ";SELECT yaml_to_json('[abc')"
|
@ -6,6 +6,15 @@ unset XDG_CONFIG_HOME
|
||||
run_cap_test ${lnav_test} -n \
|
||||
${top_srcdir}/README.md
|
||||
|
||||
run_cap_test ${lnav_test} -n -c ':goto #screenshot' \
|
||||
${top_srcdir}/README.md
|
||||
|
||||
run_cap_test ${lnav_test} -n ${top_srcdir}/README.md#screenshot
|
||||
|
||||
run_cap_test ${lnav_test} -n ${test_dir}/non-existent:4
|
||||
|
||||
run_cap_test ${lnav_test} -n ${top_srcdir}/README.md:-4
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ':goto 115' \
|
||||
-c ";SELECT top_meta FROM lnav_views WHERE name = 'text'" \
|
||||
|
Loading…
Reference in New Issue
Block a user