Markdown Lint Plugin (@lapis-notes/markdown-lint)
Source: packages/plugins/plugin-markdown-lint/src/
Package: @lapis-notes/markdown-lint. Manifest id: lapis-markdown-lint.
The Markdown Lint plugin is an external official plugin that owns markdown diagnostics provider registration. It separates markdownlint ownership from the main Markdown editing plugin while preserving the existing editor diagnostics UI and host fallback behavior.
The plugin now also owns vault-scoped markdownlint rule suppression settings, per-diagnostic ignore actions, and markdownlint quick-fix surfacing for fixable diagnostics.
Registration
- Official registry install under
/.obsidian/plugins/lapis-markdown-lint. - Listed under Core plugins when installed provenance is official, with enablement persisted through the existing external plugin enablement file.
- Declarative manifest
language-servicecontribution with one provider binding. - Declarative manifest-only configuration contribution for vault-scoped markdown-lint settings.
- Registered as an optional external official plugin, so optional-core Safe Mode disables it while community-plugin Safe Mode does not.
- No views, commands, or bespoke settings tabs.
- Exports a concrete
MarkdownLintPluginsubclass and registers exactly one MarkdownLanguageServiceProviderduring pluginonload().
Provider Selection
- On browser/PWA hosts, the plugin registers the worker-backed markdownlint provider from
@lapis-notes/language-service, whose worker bundles markdownlint runtime entry points for browser module resolution. - On Electron hosts where the native
language-servicecapability is available and responsive, the plugin registers a native markdownlint provider that talks todesktop_ls_*IPC through the shared native desktop bridge. - The provider keeps
priority: 100on Electron so native markdownlint outranks the browser worker while preserving worker fallback when the native capability is unavailable. - Both runtime paths expose markdown diagnostics and markdown code actions for fixable markdownlint rules.
Settings
- The plugin contributes a vault-scoped
markdown-lint.disabledRulesarray in app configuration. - Each configured rule ID is mapped to
falsein the effective markdownlint config for both worker and native providers. - Inline markdownlint suppression comments continue to work because the underlying markdownlint library still evaluates them during linting.
The first implementation deliberately limits persisted settings to rule suppression. Full markdownlint rule-option passthrough and external config-file loading remain out of scope.
Quick Fixes And Ignores
Markdownlint diagnostics with rule ids expose three code actions through the shared Lapis lint tooltip:
Fix markdownlint <RULE>Ignore markdownlint <RULE> on next lineIgnore markdownlint <RULE> for this file
Ignore-next-line inserts a markdownlint-disable-next-line HTML comment immediately before the offending content line, but it is not offered for diagnostics inside leading YAML frontmatter because HTML comments are not valid frontmatter content. File-level ignores insert markdownlint-disable-file before the first real content line, after any leading YAML frontmatter, and include MD041 when needed so the inserted comment does not create a new first-line-heading warning.
Lapis MD018 replacement
In-app lint disables stock markdownlint MD018 and registers a Lapis custom replacement through @lapis-notes/language-service (createMarkdownlintLintOptions()).
The replacement registers as MD018-lapis in markdownlint because built-in rule names cannot be reused, but diagnostics surface as MD018 so vault disabledRules, inline markdownlint-disable MD018 comments, and editor doc links continue to work.
Tag-aware behavior:
- Exempt whole-line lowercase Lapis tags such as
#task,#project/roadmap, and indented tags with up to three leading spaces. - Exempt line-start tag prefixes such as
#task Buy milk. - Do not report valid spaced ATX headings such as
# Intro,## Start Here, and### Deep Dive. - Still report malformed ATX headings such as
#Introduction,##Section,###foo, and#Heading. - Preserve the quick-fix that inserts a space after the hash run for remaining violations.
Tag exemption matches Lapis inline tag conventions: optional up-to-three-space indent, single #, lowercase letter first, then lowercase letters, digits, _, -, and /.
Both the browser worker and Electron sidecar use the same shared lint options helper so behavior stays aligned across hosts. They also share the same markdownlint runtime helper for diagnostic mapping, code-action generation, inline suppression alias normalization, and applyFixes(...)-based edit generation so fixes preserve frontmatter and edit the real violation line instead of rewriting from the top of the file.
Package Boundary
- Owns markdown diagnostics provider registration only.
- Owns markdown quick-fix and ignore-action generation for markdownlint diagnostics.
- Does not own Markdown editor views, preview rendering, metadata extraction, or CodeMirror lint UI.
- Does not own Electron sidecar lifecycle or IPC handlers; the desktop host still owns those capabilities.
Runtime Contract
- Consumes
app.languageServices.registerProvider(...). - Consumes
app.configuration.getConfiguration()to resolve vault-scoped disabled rules on demand. - Uses
@lapis-notes/language-servicefor both worker-backed and native-bridge markdown provider factories. - Probes the shared native desktop bridge through
@lapis-notes/apibefore attaching the native markdown provider.
This makes markdownlint a plugin-owned feature even when the underlying implementation uses a host-owned Electron sidecar.
Current Limitations
- Host-aware selection between worker-backed and native-desktop markdownlint still lives in package code rather than purely in manifest metadata.
- The current settings treatment is the shared Core plugins surface, not a richer dedicated markdownlint UI.
- Quick fixes currently surface per-diagnostic markdownlint edits; workspace-wide fix-all and formatter parity remain separate UX work.