Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: formatting YAML properties and exporting a cjs build #12

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ const editor = monaco.editor.create(el, {
})
```

## VS Code Extension

The exported `formatter` and `getDocumentFoldingRanges` functions are also utilized in [@nuxtlabs/vscode-mdc](https://github.com/nuxtlabs/vscode-mdc) to provide the functionality to the [MDC VS Code extension](https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc).

## 💻 Development

- Clone repository
Expand Down
21 changes: 21 additions & 0 deletions build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineBuildConfig } from 'unbuild'

// https://github.com/unjs/unbuild?tab=readme-ov-file#configuration
export default defineBuildConfig({
name: '@nuxtlabs/monarch-mdc',
// Each separate plugin's entry file should be listed here
entries: [
'./src/index',
],
externals: [
'monaco-editor-core',
],
// Generates .d.ts declaration file(s)
declaration: true,
// Clean the output directory before building
clean: true,
rollup: {
// Export as CommonJS module, primarily for accessing the formatter in @nuxtlabs/vscode-mdc
emitCJS: true,
},
})
11 changes: 1 addition & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.3.0",
"license": "MIT",
"description": "Integrate MDC syntax with Monaco Editor",
"main": "./dist/index.mjs",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
Expand Down Expand Up @@ -41,14 +41,5 @@
"npm": {
"publish": false
}
},
"build": {
"entries": [
"./src/index"
],
"externals": [
"monaco-editor-core"
],
"declaration": true
}
}
57 changes: 12 additions & 45 deletions src/folding-provider.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,22 @@
import type { languages, editor } from 'monaco-editor-core'
import { getDocumentFoldingRanges } from './get-document-folding-ranges'

/**
* Provides folding ranges for the MDC language in the Monaco editor.
*
* @param {monaco.editor.ITextModel} model - The text model to provide folding ranges for.
* @returns {languages.ProviderResult<languages.FoldingRange[]>} An array of folding ranges for the editor.
*
* The function identifies folding ranges based on:
* - Custom block components defined by start tags (e.g., "::container" or ":::button")
* and end tags (e.g., "::" or ":::" with matching opening tag level).
* - Markdown code blocks delimited by triple backticks (```) or tildes (~~~).
* @param {editor.ITextModel} model - The text model for which folding ranges are to be provided.
* @returns A promise that resolves to an array of folding ranges.
*/
export const foldingProvider = (model: editor.ITextModel): languages.ProviderResult<languages.FoldingRange[]> => {
const ranges = [] // Array to store folding ranges
const stack = [] // Stack to manage nested block components
const lines = model.getLinesContent() // Retrieve all lines
let insideCodeBlock = false // Flag to track if inside a code block

for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) {
const line = lines[lineNumber].trim() // Remove extra whitespace

// Check if the current line starts or ends a markdown code block
if (/^\s*(?:`{3,}|~{3,})/.test(line)) {
insideCodeBlock = !insideCodeBlock // Toggle code block mode
continue // Skip further processing for this line
}

// Skip processing lines inside a markdown code block
if (insideCodeBlock) {
continue
}

// Match the start tag (e.g., "::container" or ":::button")
const startMatch = line.match(/^\s*:{2,}([\w-]+)/)
if (startMatch) {
// Push start block onto the stack
stack.push({ start: lineNumber + 1, tagName: startMatch[1] }) // Save 1-based line number and tag name
continue // Skip further processing for this line
}

// Match the end tag (e.g., "::" or ":::" with matching opening tag level)
const endMatch = line.match(/^\s*:{2,}$/)
if (endMatch && stack.length > 0) {
const lastBlock = stack.pop() // Retrieve the last unmatched start block
ranges.push({
start: lastBlock?.start ?? 0, // Block start line (1-based)
end: lineNumber + 1, // Current line as block end (1-based)
})
}
const documentAdapter = {
getLine: (lineNumber: number) => model.getLineContent(lineNumber + 1),
lineCount: model.getLineCount(),
}

// Return all folding ranges to the editor
return ranges
const ranges = getDocumentFoldingRanges(documentAdapter)

return ranges.map(range => ({
start: range.start + 1,
end: range.end + 1,
}))
}
8 changes: 8 additions & 0 deletions src/formatter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,10 @@ styles: >
:::level-2
:inline2{prop="value"}
::::level-3
---
foo: "bar"
child-foo: "child-bar"
---
content
::::
:::
Expand All @@ -441,6 +445,10 @@ content
:::level-2
:inline2{prop="value"}
::::level-3
---
foo: "bar"
child-foo: "child-bar"
---
content
::::
:::
Expand Down
10 changes: 9 additions & 1 deletion src/formatter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* !Important: The exported `formatter` function in this file should remain unbound to monarch as it
* cane be used standalone to format MDC content strings. The function is also utilized in
* the `@nuxtlabs/vscode-mdc` VSCode extension https://github.com/nuxtlabs/vscode-mdc.
*
* Any changes to the function signature or behavior should be tested and verified in the extension.
*/

/**
* Formatter Options
*/
Expand Down Expand Up @@ -198,7 +206,7 @@ export const formatter = (content: string, { tabSize = 2, isFormatOnType = false
// Adjust indentation for YAML block content based on the base indent level
if (yamlState.baseIndent !== null) {
const relativeIndent = indent - yamlState.baseIndent
formattedLines[formattedIndex++] = getIndent(parentIndent + relativeIndent) + trimmedContent
formattedLines[formattedIndex++] = getIndent(Math.max(yamlState.baseIndent, parentIndent + relativeIndent)) + trimmedContent
continue
}
}
Expand Down
102 changes: 102 additions & 0 deletions src/get-document-folding-ranges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* !Important: The exported `getDocumentFoldingRanges` function in this file is also utilized in
* the `@nuxtlabs/vscode-mdc` VSCode extension https://github.com/nuxtlabs/vscode-mdc.
*
* Any changes to the function signature or behavior should be tested and verified in the extension.
*/

/** Represents a text document, providing methods to access its content. */
interface TextDocument {
/**
* Retrieves the content of a specific line in the document.
* @param lineNumber - The zero-based line number to retrieve.
* @returns The content of the specified line.
*/
getLine: (lineNumber: number) => string

/* The total number of lines in the document. */
lineCount: number
}

/**
* A block of code that can be folded in an editor.
*
* @interface FoldingBlock
* @property {number} start - The starting line number of the folding block.
* @property {string} tagName - The tag name associated with the folding block.
* @property {number} colons - The number of colons in the folding block.
*/
interface FoldingBlock {
start: number
tagName: string
colons: number
}

/**
* A range in a text document that can be folded.
*
* @interface FoldingRange
* @property {number} start - The zero-based line number where the folding starts.
* @property {number} end - The zero-based line number where the folding ends.
*/
interface FoldingRange {
start: number
end: number
}

/**
* Generates the folding ranges for a given text document. This function is designed to be used with
* text documents that follow the Monarch or TextMate syntax highlighting conventions.
*
* @param {TextDocument} document - The text document to compute folding ranges for.
* @param {(lineNumber: number) => string} document.getLine - A function that returns the content of a line given its line number.
* @param {number} document.lineCount - The total number of lines in the document.
* @returns {FoldingRange[]} - An array of FoldingRange objects representing the folding regions in the document.
*/
export const getDocumentFoldingRanges = (document: TextDocument): FoldingRange[] => {
const ranges: FoldingRange[] = []
const stack: FoldingBlock[] = []
let insideCodeBlock = false

for (let lineNumber = 0; lineNumber < document.lineCount; lineNumber++) {
const line = document.getLine(lineNumber).trim()

// Check for code block markers
if (/^\s*(?:`{3,}|~{3,})/.test(line)) {
insideCodeBlock = !insideCodeBlock
continue
}

// Skip processing lines inside a markdown code block
if (insideCodeBlock) {
continue
}

// Match start tags
const startMatch = line.match(/^\s*(:{2,})([\w-]+)/)
if (startMatch) {
stack.push({
start: lineNumber,
tagName: startMatch[2],
colons: startMatch[1].length,
})
continue
}

// Match end tags
const endMatch = line.match(/^\s*(:{2,})$/)
if (endMatch && stack.length > 0) {
const colonCount = endMatch[1].length
const lastBlock = stack[stack.length - 1]
if (lastBlock && lastBlock.colons === colonCount) {
stack.pop()
ranges.push({
start: lastBlock.start,
end: lineNumber,
})
}
}
}

return ranges
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,4 @@ export const language = <languages.IMonarchLanguage>{

export { formatter } from './formatter'
export { foldingProvider } from './folding-provider'
export { getDocumentFoldingRanges } from './get-document-folding-ranges'