obsidian-parser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c404193f58a335e936da522026f3368b2b7c42d99776fd3910d7667c948a8a54
4
+ data.tar.gz: 3b685ae208f985cba65c573f14fa199ea3a40d9f3d392a0103dbc2710178dab1
5
+ SHA512:
6
+ metadata.gz: 2ab90cafe12e33f2b808f9ad4f4e90617df034ecee71cc1ec5d27b7595767c348f6cad0d52272e3e55b376a902f78b744c080044bc674f981727a1469b6bb08f
7
+ data.tar.gz: 6028a69c8f9fd5bdc65df4bdd467b2182492613539c4e4ed373137727b831253b7ee2eb6d6a7b9779aa8555b6764bb1f5644846c65f6393fe373b943c6a952ed
@@ -0,0 +1,20 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v2.3.0
4
+ hooks:
5
+ - id: end-of-file-fixer
6
+ - id: trailing-whitespace
7
+ - repo: local
8
+ hooks:
9
+ - id: standardrb
10
+ name: bundle exec rake standard:fix
11
+ entry: bundle exec rake standard:fix
12
+ language: system
13
+ pass_filenames: false
14
+ - repo: local
15
+ hooks:
16
+ - id: rspec
17
+ name: bundle exec rspec
18
+ entry: bundle exec rspec
19
+ language: system
20
+ pass_filenames: false
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.1
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
3
+ ruby_version: 2.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-07-10
4
+
5
+ - Initial release: return a tree of notes
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in obsidian-parser.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "standard", "~> 1.3"
data/Gemfile.lock ADDED
@@ -0,0 +1,82 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ obsidian-parser (0.1.0)
5
+ kramdown (~> 2.4)
6
+ kramdown-parser-gfm (~> 1.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.2)
12
+ diff-lcs (1.5.0)
13
+ json (2.6.3)
14
+ kramdown (2.4.0)
15
+ rexml
16
+ kramdown-parser-gfm (1.1.0)
17
+ kramdown (~> 2.0)
18
+ language_server-protocol (3.17.0.3)
19
+ lint_roller (1.1.0)
20
+ parallel (1.23.0)
21
+ parser (3.2.2.3)
22
+ ast (~> 2.4.1)
23
+ racc
24
+ racc (1.7.1)
25
+ rainbow (3.1.1)
26
+ rake (13.0.6)
27
+ regexp_parser (2.8.1)
28
+ rexml (3.2.5)
29
+ rspec (3.12.0)
30
+ rspec-core (~> 3.12.0)
31
+ rspec-expectations (~> 3.12.0)
32
+ rspec-mocks (~> 3.12.0)
33
+ rspec-core (3.12.2)
34
+ rspec-support (~> 3.12.0)
35
+ rspec-expectations (3.12.3)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.12.0)
38
+ rspec-mocks (3.12.5)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.12.0)
41
+ rspec-support (3.12.1)
42
+ rubocop (1.52.1)
43
+ json (~> 2.3)
44
+ parallel (~> 1.10)
45
+ parser (>= 3.2.2.3)
46
+ rainbow (>= 2.2.2, < 4.0)
47
+ regexp_parser (>= 1.8, < 3.0)
48
+ rexml (>= 3.2.5, < 4.0)
49
+ rubocop-ast (>= 1.28.0, < 2.0)
50
+ ruby-progressbar (~> 1.7)
51
+ unicode-display_width (>= 2.4.0, < 3.0)
52
+ rubocop-ast (1.29.0)
53
+ parser (>= 3.2.1.0)
54
+ rubocop-performance (1.18.0)
55
+ rubocop (>= 1.7.0, < 2.0)
56
+ rubocop-ast (>= 0.4.0)
57
+ ruby-progressbar (1.13.0)
58
+ standard (1.30.1)
59
+ language_server-protocol (~> 3.17.0.2)
60
+ lint_roller (~> 1.0)
61
+ rubocop (~> 1.52.0)
62
+ standard-custom (~> 1.0.0)
63
+ standard-performance (~> 1.1.0)
64
+ standard-custom (1.0.1)
65
+ lint_roller (~> 1.0)
66
+ standard-performance (1.1.1)
67
+ lint_roller (~> 1.1)
68
+ rubocop-performance (~> 1.18.0)
69
+ unicode-display_width (2.4.2)
70
+
71
+ PLATFORMS
72
+ x86_64-darwin-20
73
+ x86_64-linux
74
+
75
+ DEPENDENCIES
76
+ obsidian-parser!
77
+ rake (~> 13.0)
78
+ rspec (~> 3.0)
79
+ standard (~> 1.3)
80
+
81
+ BUNDLED WITH
82
+ 2.4.6
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Mat Moore
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # Obsidian::Parser
2
+
3
+ A (work in progress) gem to parse notes created with the Obsidian note-taking tool.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ $ bundle add obsidian-parser
10
+
11
+ If bundler is not being used to manage dependencies, install the gem by executing:
12
+
13
+ $ gem install obsidian-parser
14
+
15
+ ## Usage
16
+ WARNING: This API is not yet finalized.
17
+
18
+ ```ruby
19
+ parser = Obsidian::Parser.new(Pathname.new("/path/to/vault"))
20
+
21
+ puts parser.index.notes
22
+ # -> [ Note(title: "README", slug: "README") ]
23
+
24
+ puts parser.index.directories
25
+ # -> [ Index(title: "Drafts", slug: "Drafts"),
26
+ # Index(title: "Projects", slug: "Projects") ]
27
+ ```
28
+
29
+ ## Development
30
+
31
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
32
+
33
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
34
+
35
+ ## Contributing
36
+
37
+ Bug reports and pull requests are welcome on GitHub at https://github.com/matmoore/obsidian-parser.
38
+
39
+ ## Resources and similar projects
40
+
41
+ - [Obsidian link formats](https://help.obsidian.md/Linking+notes+and+files/Internal+links)
42
+ - [Obisidian metadata format](https://help.obsidian.md/Editing+and+formatting/Metadata)
43
+ - [Obsidian flavored markdown](https://help.obsidian.md/Editing+and+formatting/Obsidian+Flavored+Markdown)
44
+ - [Is there a parser/renderer reference spec? (No)](https://forum.obsidian.md/t/is-there-a-parser-renderer-reference-spec/29504/4)
45
+ - [Obsidian-Markdown-Parser](https://github.com/danymat/Obsidian-Markdown-Parser) (Python)
46
+
47
+ ## License
48
+
49
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "standard/rake"
9
+
10
+ task default: %i[spec standard]
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Obsidian
4
+ class MarkdownConverter
5
+ def initialize(document)
6
+ @document = document
7
+ end
8
+
9
+ def extract_links
10
+ _extract_links(document.root)
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :document
16
+
17
+ def _extract_links(element)
18
+ if element.type == :a
19
+ [[element.attr["href"], _extract_text_content(element)]]
20
+ elsif !element.children.empty?
21
+ element.children.flat_map { |child| _extract_links(child) }
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ def _extract_text_content(element)
28
+ if element.type == :text
29
+ element.value
30
+ elsif !element.children.empty?
31
+ element.children.map { |child| _extract_text_content(child) }.join
32
+ else
33
+ ""
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kramdown"
4
+ require "kramdown-parser-gfm"
5
+
6
+ module Obsidian
7
+ module ObsidianFlavoredMarkdown
8
+ WIKILINK_SYNTAX = %r{
9
+ \[\[
10
+ (?<target>[^\]\#|]*) # link target
11
+ (?:
12
+ \#(?<fragment>[^|\]]*) # optional heading fragment
13
+ )?
14
+ (?:
15
+ \|(?<text>[^\]]*) # optional link display text
16
+ )?
17
+ \]\]
18
+ }x
19
+
20
+ # Convert Obsidian-flavored-markdown syntax to something parseable
21
+ # (i.e. with Github-flavored-markdown syntax)
22
+ def self.normalize(markdown_text)
23
+ markdown_text.gsub(WIKILINK_SYNTAX) do |s|
24
+ text = $~[:text]
25
+ target = $~[:target]
26
+ fragment = $~[:fragment]
27
+ display_text = text.nil? ? target.split("/").last : text
28
+ href = fragment.nil? ? target : "#{target}##{fragment}"
29
+
30
+ "[#{display_text}](#{href})"
31
+ end
32
+ end
33
+
34
+ # Parse links from obsidian-flavored-markdown text
35
+ def self.parse(markdown_text)
36
+ document = Kramdown::Document.new(normalize(markdown_text), input: "GFM")
37
+ Obsidian::MarkdownConverter.new(document)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Obsidian
4
+ class Parser
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser/version"
4
+ require_relative "parser/markdown_converter"
5
+ require_relative "parser/obsidian_flavored_markdown"
6
+
7
+ module Obsidian
8
+ class Error < StandardError; end
9
+
10
+ def self.build_slug(title, parent_slug)
11
+ (parent_slug == "") ? title : "#{parent_slug}/#{title}"
12
+ end
13
+
14
+ class Note
15
+ def initialize(title, slug, last_modified)
16
+ # TODO: check frontmatter for titles as well
17
+ @title = title
18
+ @slug = slug
19
+ @last_modified = last_modified
20
+ end
21
+
22
+ def inspect
23
+ "Note(title: #{title.inspect}, slug: #{slug.inspect})"
24
+ end
25
+
26
+ attr_reader :title
27
+ attr_reader :slug
28
+ attr_reader :last_modified
29
+ end
30
+
31
+ class Index
32
+ def initialize(title = "", slug = "")
33
+ @title = title
34
+ @slug = slug
35
+ @notes = []
36
+ @directories = {}
37
+ end
38
+
39
+ def add_directory(title)
40
+ new_slug = Obsidian.build_slug(title, slug)
41
+ @directories[title] ||= Index.new(title, new_slug)
42
+ end
43
+
44
+ def add_note(title, parent_slug, last_modified)
45
+ slug = Obsidian.build_slug(title, parent_slug)
46
+ directory = nested_directory(parent_slug.split("/"))
47
+ note = Note.new(title, slug, last_modified)
48
+
49
+ directory.notes << note
50
+ end
51
+
52
+ def inspect
53
+ "Index(title: #{title.inspect}, slug: #{slug.inspect})"
54
+ end
55
+
56
+ def directories
57
+ @directories.values
58
+ end
59
+
60
+ attr_reader :notes
61
+ attr_reader :title
62
+ attr_reader :slug
63
+
64
+ private
65
+
66
+ def nested_directory(path_components)
67
+ path_components.reduce(self) { |index, subdirectory| index.add_directory(subdirectory) }
68
+ end
69
+ end
70
+
71
+ class Parser
72
+ attr_reader :index
73
+
74
+ def initialize(vault_directory)
75
+ @index = Index.new("", "")
76
+
77
+ vault_directory.glob("**/*.md").each do |path|
78
+ dirname, basename = path.relative_path_from(vault_directory).split
79
+
80
+ if basename != "index.md" && basename != "."
81
+ title = basename.to_s.gsub(/\.md\z/, "")
82
+ parent_slug = dirname.to_s.gsub(/\A\.\/?/, "")
83
+ @index.add_note(title, parent_slug, path.mtime)
84
+ end
85
+ end
86
+
87
+ # TODO: capture links between notes
88
+ end
89
+
90
+ def notes
91
+ table_of_contents.map(&:first)
92
+ end
93
+
94
+ def walk_tree(index, level = 0, &block)
95
+ index.directories.sort_by(&:title).each do |note|
96
+ block.call(note, level)
97
+ walk_tree(note, level + 1, &block)
98
+ end
99
+
100
+ index.notes.sort_by(&:title).each do |note|
101
+ block.call(note, level)
102
+ end
103
+ end
104
+
105
+ def table_of_contents
106
+ result = []
107
+ walk_tree(index) { |note, level| result << [note, level] }
108
+ result
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/obsidian/parser/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "obsidian-parser"
7
+ spec.version = Obsidian::Parser::VERSION
8
+ spec.authors = ["Mat Moore"]
9
+ spec.email = ["matmoore@users.noreply.github.com"]
10
+
11
+ spec.summary = "Parse notes created with the Obsidian note-taking tool."
12
+ # spec.description = "TODO: Write a longer description or delete this line."
13
+
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
+
19
+ spec.metadata["source_code_uri"] = "https://github.com/matmoore/obsidian-parser"
20
+ spec.metadata["changelog_uri"] = "https://github.com/matmoore/obsidian-parser/tree/main/CHANGELOG.md"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ # Uncomment to register a new dependency of your gem
34
+ spec.add_dependency "kramdown", "~> 2.4"
35
+ spec.add_dependency "kramdown-parser-gfm", "~> 1.1"
36
+
37
+ # For more information and examples about making a new gem, check out our
38
+ # guide at: https://bundler.io/guides/creating_gem.html
39
+ end
@@ -0,0 +1,6 @@
1
+ module Obsidian
2
+ module Parser
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: obsidian-parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mat Moore
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-07-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kramdown
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kramdown-parser-gfm
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ description:
42
+ email:
43
+ - matmoore@users.noreply.github.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".pre-commit-config.yaml"
49
+ - ".rspec"
50
+ - ".ruby-version"
51
+ - ".standard.yml"
52
+ - CHANGELOG.md
53
+ - Gemfile
54
+ - Gemfile.lock
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - lib/obsidian/parser.rb
59
+ - lib/obsidian/parser/markdown_converter.rb
60
+ - lib/obsidian/parser/obsidian_flavored_markdown.rb
61
+ - lib/obsidian/parser/version.rb
62
+ - obsidian-parser.gemspec
63
+ - sig/obsidian/parser.rbs
64
+ homepage:
65
+ licenses:
66
+ - MIT
67
+ metadata:
68
+ source_code_uri: https://github.com/matmoore/obsidian-parser
69
+ changelog_uri: https://github.com/matmoore/obsidian-parser/tree/main/CHANGELOG.md
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 2.6.0
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.4.6
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Parse notes created with the Obsidian note-taking tool.
89
+ test_files: []