mirror of
https://github.com/alamion/obsidian-jira-sync.git
synced 2026-07-22 05:43:04 +00:00
File indicators update
- File reading for `jira-sync-*` is now much more time efficient. - Readmes updated in terms of how file body indicators work. - Template example reworked. - Fixed how hidden indicators look like in Live Preview (no more glitching lines)
This commit is contained in:
parent
a4ec70fecd
commit
b43f731b0a
9 changed files with 315 additions and 130 deletions
|
|
@ -17,16 +17,57 @@ Without a template, such a page will be completely empty except for its title, s
|
|||
|
||||
#### Template
|
||||
The template can consist of several different parts:
|
||||
1. **formatter** - meta-information at the top of the screen. When specifying keys for it and using any `Get issues from Jira` variant, they will be populated with corresponding fields from the response data.
|
||||
2. **body** - the main content of the page. When using indicators like `jira-sync-section-*`, `jira-sync-line-*`, or `jira-sync-inline-start-*`, they will be filled with the corresponding fields from the response data. The difference between these options is as follows: `line` reads and writes values from the current line, separating the indicator and value with a space. `section` reads values from multiple lines after the indicator, stopping only at another indicator or a heading. `inline` allows indicators to be placed anywhere in the text, not strictly at the beginning of lines. All indicators are invisible until highlighted with the mouse, providing a cleaner editing experience.
|
||||
|
||||
##### Frontmatter
|
||||
Meta-information at the top of the screen. When specifying keys for it and using any `Get issues from Jira` variant, they will be populated with corresponding fields from the response data.
|
||||
|
||||
##### Body
|
||||
The main content of the page. When using indicators like `jira-sync-"type"-*`, they will be filled with the corresponding fields from the response data.
|
||||
|
||||
The difference between these options is as follows:
|
||||
|
||||
- `line` reads and writes values from the current line.
|
||||
|
||||
Example:
|
||||
```md
|
||||
The responsible person for this task is `jira-sync-line-assignee` Bob
|
||||
The description is `jira-sync-line-description` Some description
|
||||
```
|
||||
- `section` reads values from multiple lines after the indicator, stopping only at any other indicator or a heading.
|
||||
|
||||
Example:
|
||||
```md
|
||||
### Responsible `jira-sync-section-assignee`
|
||||
Bob
|
||||
### Description `jira-sync-section-description`
|
||||
Some description
|
||||
`jira-sync-section-customfield_10842`
|
||||
...
|
||||
```
|
||||
- `inline` allows indicator of start and end to be placed anywhere in the text. Must have an ending part of indicator (`jira-sync-end`).
|
||||
|
||||
Example:
|
||||
```md
|
||||
The responsible person for this task is `jira-sync-inline-start-assignee`Bob`jira-sync-end`, and the description is `jira-sync-inline-start-description`Some description`jira-sync-end`.
|
||||
```
|
||||
- `block` is basically the same as `inline`, but with line break before and after indicators.
|
||||
|
||||
Example:
|
||||
```md
|
||||
The responsible person for this task is `jira-sync-block-assignee`
|
||||
Bob
|
||||
`jira-sync-end`, and the description is `jira-sync-block-description`
|
||||
Some description
|
||||
`jira-sync-end`.
|
||||
```
|
||||
|
||||
An example can be found in [[docs/template_example]].
|
||||
|
||||
It is highly recommended to specify basic values in the template's formatter: `key` - the Jira task ID used for updates, `summary` - the Jira task title, and `status` - the current status of the task in Jira.
|
||||
It is highly recommended to specify the following basic values in the template's formatter: `key` - the Jira task ID used for updates, `summary` - the Jira task title, and `status` - the current status of the task in Jira.
|
||||
|
||||
The formatter takes priority and will overwrite file content values when updating a task in Jira if a field's value is present in both as formatter saves initial format of value and when putting it in body we parse it to string.
|
||||
|
||||
Not all fields are predefined, and some may need adjustments. For example, the template provided in [[docs/template_example]] will not correctly retrieve `progressPercent` and `creator` from Jira, even though these fields exist. To fix this, refer to the advanced usage section below.
|
||||
Not all fields are predefined, and some may need adjustments. To add any custom nonusual fields, features of their representation in .md format and how to update their info in Jira, refer to the advanced usage section below.
|
||||
|
||||
### Commands
|
||||
|
||||
|
|
|
|||
|
|
@ -17,16 +17,57 @@
|
|||
|
||||
#### Шаблон
|
||||
Шаблон можно составить из нескольких разных частей:
|
||||
1. **formatter** - он же метаинформация сверху экрана. При указании ключей для него и любом варианте `Get issues from Jira` они будут заполняться соответствующими полями из ответных данных.
|
||||
2. **body** - основное содержимое страницы. При написании указателя вида `jira-sync-section-*`, `jira-sync-line-*` или `jira-sync-inline-start-*` они будут заполняться соответствующими полями из ответных данных. Разница между этими вариантами в следующем: `line` читает и пишет значение из текущей строки, разделяя указатель и значение пробелом. `section` читает значение из ряда строк после указателя, останавливаясь только при другом указателе или заголовке. `inline` позволяет индикаторам располагаться где угодно, а не строго в начале строки. Все индикаторы являются невидимыми пока не выделишь их мышкой, обеспечивая более чистый опыт редактирования.
|
||||
|
||||
Пример можно посмотреть в [[docs/template_example]]
|
||||
##### Frontmatter
|
||||
Оно же метаинформация сверху экрана. При указании ключей для него и любом варианте `Get issues from Jira` они будут заполняться соответствующими полями из ответных данных.
|
||||
|
||||
Настоятельно рекомендуется указать в formatter шаблоона базовые значения: `key` - id задачи в Jira, по которому производится обновление, `summary` - название задачи в Jira, `status` - текущий статус задачи в Jira.
|
||||
##### Body
|
||||
Основное содержание страницы. При использовании индикаторов типа `jira-sync-"type"-*` они будут заполняться соответствующими полями из сырых данных задачи.
|
||||
|
||||
formatter имеет приоритет и будет перезаписывать значения body, если значения для одного поля имеются в обоих местах, так как formatter сохраняет изначальный тип переменной, а body конвертирует её в string.
|
||||
Разница между этими параметрами заключается в следующем:
|
||||
|
||||
Не все поля прописаны заранее и некоторые могут нуждаться в доработке. Пример предоставленный в [[docs/template_example]] например, не сможет подтянуть корректно `progressPercent` и `creator` из Jira, хотя такие поля существуют. Чтобы это исправить, нужно отбратиться к разделу продвинутого использования ниже.
|
||||
- `line` считывает и записывает значения из текущей строки.
|
||||
|
||||
Пример:
|
||||
```md
|
||||
Ответственным за эту задачу является `jira-sync-line-assignee` Боб.
|
||||
Описание: `jira-sync-line-description` Некоторое описание.
|
||||
```
|
||||
- `section` считывает значения из нескольких строк после индикатора, останавливаясь только на любом другом индикаторе или заголовке.
|
||||
|
||||
Пример:
|
||||
```md
|
||||
### Ответственный `jira-sync-section-assignee`
|
||||
Боб
|
||||
### Описание `jira-sync-section-description`
|
||||
Некоторое описание
|
||||
`jira-sync-section-customfield_10842`...
|
||||
|
||||
```
|
||||
- `inline` позволяет размещать индикатор начала и окончания в любом месте текста. Должна быть конечная часть индикатора (`jira-sync-end`).
|
||||
|
||||
Пример:
|
||||
```md
|
||||
Ответственным за эту задачу является `jira-sync-inline-start-assignee`Боб`jira-sync-end`, а описание — `jira-sync-inline-start-description`Некоторое описание`jira-sync-end`.
|
||||
```
|
||||
- `block` в основном аналогичен `inline`, но с разрывом строки перед и после индикаторов.
|
||||
|
||||
Пример:
|
||||
```md
|
||||
Ответственным за эту задачу является `jira-sync-block-assignee`
|
||||
Bob
|
||||
`jira-sync-end`, а описание — `jira-sync-block-description`
|
||||
Некоторое описание
|
||||
`jira-sync-end`.
|
||||
```
|
||||
|
||||
Пример можно найти в [[docs/template_example]].
|
||||
|
||||
Настоятельно рекомендуется указать следующие базовые значения в форматере шаблона: `key` — ID задачи Jira, используемый для обновлений, `summary` — название задачи Jira и `status` — текущий статус задачи в Jira.
|
||||
|
||||
Форматировщик имеет приоритет и перезаписывает значения содержимого файла при обновлении задачи в Jira, если значение поля присутствует в обоих, поскольку форматировщик сохраняет исходный формат значения, а при помещении его в тело мы преобразуем его в строку.
|
||||
|
||||
Не все поля являются предопределенными, и некоторые из них могут потребовать дополнительной настройки. Чтобы добавить любые нестандартные поля, узнать об особенностях их представления в формате .md и о том, как обновлять их информацию в Jira, обратитесь к разделу "Продвинутое использование" ниже.
|
||||
|
||||
### Команды
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,99 @@
|
|||
---
|
||||
key:
|
||||
assignee:
|
||||
summary:
|
||||
status:
|
||||
deadline:
|
||||
link:
|
||||
progress:
|
||||
tags: []
|
||||
key: JIR-1234
|
||||
status: In Progress
|
||||
priority: Medium
|
||||
sprint: Mobile-Q2-24
|
||||
epic: API-Overhaul
|
||||
link: http://jira.local:8000/browse/JIR-1234
|
||||
---
|
||||
|
||||
### Time spent
|
||||
```timekeep
|
||||
{"entries":[]}
|
||||
```
|
||||
# Main Task Description
|
||||
|
||||
### Deadline
|
||||
`INPUT[datePicker:deadline]`
|
||||
This is a comprehensive test file for the jira-sync parser.
|
||||
|
||||
### Description
|
||||
`jira-sync-section-description`
|
||||
## Summary `jira-sync-section-summary`
|
||||
Complete API standardization and consolidation across all microservices to improve developer experience and reduce integration complexity.
|
||||
|
||||
This section continues here with more details.
|
||||
Multiple paragraphs are supported.
|
||||
|
||||
## Details Section
|
||||
|
||||
### Customer Impact `jira-sync-section-customfield_10842`
|
||||
The current API structure is too fragmented and requires standardization. Customers are experiencing difficulties integrating with our services due to:
|
||||
- Inconsistent authentication methods
|
||||
- Different response formats
|
||||
- Lack of versioning strategy
|
||||
|
||||
This impacts approximately 2,300 enterprise customers.
|
||||
|
||||
### Next Heading Should Stop Section
|
||||
This content should NOT be part of customfield_10842.
|
||||
|
||||
## Assignment Info
|
||||
|
||||
Primary assignee: `jira-sync-inline-start-assignee`Jack A.M.`jira-sync-end` with backup from `jira-sync-inline-start-assignee_backup`Sarah Chen`jira-sync-end`.
|
||||
|
||||
Reporter: `jira-sync-inline-start-reporter`Mike Johnson`jira-sync-end`
|
||||
|
||||
## Time Tracking
|
||||
|
||||
Expected time spent on the task: `jira-sync-line-originalEstimate`1w 3d
|
||||
|
||||
Actual time logged so far: `jira-sync-line-timeSpent`2d 4h
|
||||
|
||||
Remaining estimate: `jira-sync-line-remainingEstimate`5d
|
||||
|
||||
## Acceptance Criteria `jira-sync-section-customfield_10100`
|
||||
1. All endpoints follow RESTful conventions
|
||||
2. API versioning implemented via headers
|
||||
3. Consistent error handling across services
|
||||
4. OpenAPI 3.0 documentation published
|
||||
5. Migration guide for existing integrations
|
||||
|
||||
All criteria must be met before moving to Done.
|
||||
|
||||
### Technical Requirements
|
||||
This is a separate section that shouldn't be included above.
|
||||
|
||||
## Code Block Examples
|
||||
|
||||
Here's a configuration block:
|
||||
|
||||
`jira-sync-block-start-customfield_10500`
|
||||
{
|
||||
"apiVersion": "v2",
|
||||
"authentication": "OAuth2",
|
||||
"rateLimit": 1000
|
||||
}
|
||||
`jira-sync-end`
|
||||
|
||||
And another block for database config:
|
||||
|
||||
`jira-sync-block-start-customfield_10501`
|
||||
host: prod-db-01.internal
|
||||
port: 5432
|
||||
database: api_gateway
|
||||
ssl: true
|
||||
`jira-sync-end`
|
||||
|
||||
## Edge Cases Testing
|
||||
|
||||
### Empty Inline
|
||||
Status indicator: `jira-sync-inline-start-status_indicator``jira-sync-end`
|
||||
|
||||
### Whitespace Heavy Section `jira-sync-section-whitespace_test`
|
||||
|
||||
|
||||
This section has leading and trailing whitespace
|
||||
|
||||
|
||||
Should be trimmed properly before sending to Jira.
|
||||
|
||||
### Single Line Section `jira-sync-section-oneliner`
|
||||
Just one line here.
|
||||
|
||||
## Multi-line Inline Test
|
||||
Complex description: `jira-sync-inline-start-description` This is a description
|
||||
that spans multiple lines
|
||||
with various formatting `jira-sync-inline-end` and continues here.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "jira-sync",
|
||||
"name": "Jira Issue Manager",
|
||||
"version": "1.4.0",
|
||||
"version": "1.4.1",
|
||||
"minAppVersion": "1.10.1",
|
||||
"description": "Get Jira issues, create and update them. Issue status and worklog management.",
|
||||
"author": "Alamion",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "obsidian-sample-plugin",
|
||||
"version": "1.4.0",
|
||||
"version": "1.4.1",
|
||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -8,4 +8,13 @@ tags:
|
|||
description:
|
||||
---
|
||||
|
||||
### Time spend
|
||||
\`\`\`timekeep
|
||||
{"entries":[]}
|
||||
\`\`\`
|
||||
|
||||
### Description
|
||||
\`jira-sync-section-description\`
|
||||
|
||||
### Other info
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ export function createJiraSyncExtension(plugin: JiraPlugin): Extension {
|
|||
for (const match of text.matchAll(/`(jira-sync-[^`]+)`/g)) {
|
||||
const start = from + match.index!;
|
||||
const end = start + match[0].length;
|
||||
const contentStart = start + 1; // без первой `
|
||||
const contentEnd = end - 1; // без последней `
|
||||
const contentStart = start; // без первой `
|
||||
const contentEnd = end; // без последней `
|
||||
|
||||
// Find code blocks to ignore
|
||||
if (this.isInsideCodeBlock(contentStart, contentEnd, codeBlocks)) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { JiraIssue } from "../interfaces";
|
||||
import {JiraIssue} from "../interfaces";
|
||||
import {jiraToMarkdown} from "./markdownHtml";
|
||||
import {Notice, TFile} from "obsidian";
|
||||
import JiraPlugin from "../main";
|
||||
|
|
@ -64,12 +64,14 @@ export async function updateJiraToLocal(
|
|||
|
||||
// Update content sections from Jira fields
|
||||
let updatedContent = fileContent;
|
||||
let updatesDict: Record<string, string> = {};
|
||||
for (const [fieldName, fieldValue] of Object.entries(syncSections)) {
|
||||
const markdownValue = jiraToMarkdown(fieldValue);
|
||||
debugLog(`Updating sync section: ${fieldName}: ${markdownValue}`);
|
||||
updatedContent = updateJiraSyncContent(updatedContent, fieldName, markdownValue);
|
||||
updatesDict[fieldName] = jiraToMarkdown(fieldValue);
|
||||
}
|
||||
|
||||
debugLog(`Updating sync sections: ${JSON.stringify(updatesDict)}`);
|
||||
updatedContent = updateJiraSyncContent(updatedContent, updatesDict);
|
||||
|
||||
return updatedContent;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +1,128 @@
|
|||
import { debugLog } from "./debugLogging";
|
||||
|
||||
const regexCache = new Map<string, RegExp>();
|
||||
const UNIFIED_REGEX = /`jira-sync-(section|line|inline-start|block-start)-([\w-]+)`([\s\S]*?)(?:`jira-sync-?[^-]*-end`|(?=`jira-sync-|\z))/g; // TODO: delete deprecated endings in a year
|
||||
|
||||
function getRegex(type: "section" | "line" | "inline", name: string): RegExp {
|
||||
const key = `${type}:${name}`;
|
||||
if (regexCache.has(key)) return regexCache.get(key)!;
|
||||
|
||||
let regex: RegExp;
|
||||
switch (type) {
|
||||
case "section":
|
||||
regex = new RegExp(
|
||||
`\`jira-sync-section-${name}\`([\\s\\S]*?)\\n?([\\s\\S]*?)(?=\\n#+ |\\n[^\\n]*\`jira-sync-|$)`,
|
||||
"g"
|
||||
);
|
||||
break;
|
||||
case "line":
|
||||
regex = new RegExp(
|
||||
`\`jira-sync-line-${name}\` *(.*?)(?=\\n|$)`,
|
||||
"g"
|
||||
);
|
||||
break;
|
||||
case "inline":
|
||||
regex = new RegExp(
|
||||
`\`jira-sync-inline-start-${name}\`([\\s\\S]*?)\`jira-sync-inline-end\``,
|
||||
"g"
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
regexCache.set(key, regex);
|
||||
return regex;
|
||||
interface ParsedBlock {
|
||||
type: "section" | "line" | "inline" | "block";
|
||||
name: string;
|
||||
content: string;
|
||||
startIndex: number;
|
||||
endIndex: number;
|
||||
fullMatch: string;
|
||||
}
|
||||
|
||||
function updateJiraSyncBlock(
|
||||
fileContent: string,
|
||||
type: "section" | "line" | "inline",
|
||||
name: string,
|
||||
content: string,
|
||||
force: boolean
|
||||
): string {
|
||||
const regex = getRegex(type, name);
|
||||
|
||||
let newBlock: string;
|
||||
switch (type) {
|
||||
case "section":
|
||||
newBlock = `\`jira-sync-section-${name}\`\n${content}`;
|
||||
break;
|
||||
case "line":
|
||||
newBlock = `\`jira-sync-line-${name}\`${content.split("\n")[0]}`;
|
||||
break;
|
||||
case "inline":
|
||||
newBlock = `\`jira-sync-inline-start-${name}\`${content}\`jira-sync-inline-end\``;
|
||||
break;
|
||||
}
|
||||
|
||||
const updated = fileContent.replace(regex, () => newBlock);
|
||||
if (updated !== fileContent) return updated;
|
||||
|
||||
if (force) {
|
||||
return fileContent + `\n${newBlock}`;
|
||||
}
|
||||
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
export function updateJiraSyncContent(
|
||||
fileContent: string,
|
||||
sectionName: string,
|
||||
markdownContent: string,
|
||||
force: boolean = false
|
||||
): string {
|
||||
fileContent = updateJiraSyncBlock(fileContent, "section", sectionName, markdownContent, force);
|
||||
fileContent = updateJiraSyncBlock(fileContent, "line", sectionName, markdownContent, force);
|
||||
fileContent = updateJiraSyncBlock(fileContent, "inline", sectionName, markdownContent, force);
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
function extractValues(
|
||||
fileContent: string,
|
||||
type: "section" | "line" | "inline"
|
||||
): Record<string, string> {
|
||||
const regex = getRegex(type, "([\\w-]+)");
|
||||
const values: Record<string, string> = {};
|
||||
|
||||
export function parseFileContent(fileContent: string): ParsedBlock[] {
|
||||
const blocks: ParsedBlock[] = [];
|
||||
let match;
|
||||
while ((match = regex.exec(fileContent)) !== null) {
|
||||
switch (type) {
|
||||
case "section":
|
||||
values[match[1]] = (match[3] || "").trim();
|
||||
break;
|
||||
case "line":
|
||||
values[match[1]] = (match[2] || "").trim();
|
||||
break;
|
||||
case "inline":
|
||||
values[match[1]] = (match[2] || "").trim();
|
||||
break;
|
||||
|
||||
// Reset regex state
|
||||
UNIFIED_REGEX.lastIndex = 0;
|
||||
|
||||
while ((match = UNIFIED_REGEX.exec(fileContent)) !== null) {
|
||||
const [fullMatch, typeRaw, name, rawContent] = match;
|
||||
let type: ParsedBlock["type"];
|
||||
let extractedContent: string;
|
||||
let actualEndIndex: number;
|
||||
|
||||
// Determine actual type and extract content
|
||||
if (typeRaw === "section") {
|
||||
type = "section";
|
||||
// Cut at first heading or another jira-sync-*
|
||||
const headingMatch = rawContent.match(/\n#+? /);
|
||||
const contentBeforeHeading = headingMatch
|
||||
? rawContent.substring(0, (headingMatch.index||0)+1)
|
||||
: rawContent;
|
||||
actualEndIndex = match.index + fullMatch.length - (rawContent.length - contentBeforeHeading.length);
|
||||
extractedContent = contentBeforeHeading.trim();
|
||||
} else if (typeRaw === "line") {
|
||||
type = "line";
|
||||
// Content is on the same line only
|
||||
const firstLine = rawContent.split("\n")[0];
|
||||
actualEndIndex = match.index + fullMatch.length - (rawContent.length - firstLine.length);
|
||||
extractedContent = firstLine.trim();
|
||||
} else if (typeRaw === "inline-start") {
|
||||
type = "inline";
|
||||
// Content between inline-start and inline-end (markers included in fullMatch)
|
||||
actualEndIndex = match.index + fullMatch.length;
|
||||
extractedContent = rawContent.trim();
|
||||
} else if (typeRaw === "block-start") {
|
||||
type = "block";
|
||||
// Content between block-start and block-end
|
||||
const lines = rawContent.split("\n");
|
||||
const contentLines = lines.slice(lines[0] === "" ? 1 : 0, -1);
|
||||
const joinedContent = contentLines.join("\n");
|
||||
actualEndIndex = match.index + fullMatch.length;
|
||||
extractedContent = joinedContent.trim();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks.push({
|
||||
type,
|
||||
name,
|
||||
content: extractedContent,
|
||||
startIndex: match.index,
|
||||
endIndex: actualEndIndex,
|
||||
// indexesContent: fileContent.substring(match.index, actualEndIndex),
|
||||
fullMatch
|
||||
});
|
||||
}
|
||||
|
||||
return values;
|
||||
return blocks;
|
||||
}
|
||||
|
||||
export function extractAllJiraSyncValuesFromContent(
|
||||
fileContent: string
|
||||
): Record<string, string> {
|
||||
const sections = extractValues(fileContent, "section");
|
||||
const lines = extractValues(fileContent, "line");
|
||||
const inlines = extractValues(fileContent, "inline");
|
||||
const blocks = parseFileContent(fileContent);
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
for (const block of blocks) {
|
||||
result[block.name] = block.content;
|
||||
}
|
||||
|
||||
const result = { ...sections, ...lines, ...inlines };
|
||||
debugLog(`extracted content from file: ${JSON.stringify(result)}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function updateJiraSyncContent(
|
||||
fileContent: string,
|
||||
updates: Record<string, string>
|
||||
): string {
|
||||
const blocks = parseFileContent(fileContent);
|
||||
|
||||
// Sort blocks in reverse order to replace from end to start
|
||||
// This prevents index shifting issues
|
||||
blocks.sort((a, b) => b.startIndex - a.startIndex);
|
||||
|
||||
let result = fileContent;
|
||||
|
||||
for (const block of blocks) {
|
||||
if (!(block.name in updates)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newContent = updates[block.name];
|
||||
let newBlock: string;
|
||||
|
||||
switch (block.type) {
|
||||
case "section":
|
||||
newBlock = `\`jira-sync-section-${block.name}\`\n${newContent}\n`;
|
||||
break;
|
||||
case "line":
|
||||
newBlock = `\`jira-sync-line-${block.name}\`${newContent.split("\n")[0]}`;
|
||||
break;
|
||||
case "inline":
|
||||
newBlock = `\`jira-sync-inline-start-${block.name}\`${newContent}\`jira-sync-end\``;
|
||||
break;
|
||||
case "block":
|
||||
newBlock = `\`jira-sync-block-start-${block.name}\`\n${newContent}\n\`jira-sync-end\``;
|
||||
break;
|
||||
}
|
||||
|
||||
// Replace from end to start to avoid index shifting
|
||||
result = result.substring(0, block.startIndex) + newBlock + result.substring(block.endIndex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue