mirror of
https://github.com/go-gitea/gitea.git
synced 2025-02-20 11:43:57 +08:00
Merge branch 'main' into agit/update
This commit is contained in:
commit
3e0c5302fa
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -13,5 +13,5 @@ contact_links:
|
||||
url: https://docs.gitea.com/help/faq
|
||||
about: Please check if your question isn't mentioned here.
|
||||
- name: Crowdin Translations
|
||||
url: https://crowdin.com/project/gitea
|
||||
url: https://translate.gitea.com
|
||||
about: Translations are managed here.
|
||||
|
@ -182,7 +182,7 @@ Here's how to run the test suite:
|
||||
|
||||
## Translation
|
||||
|
||||
All translation work happens on [Crowdin](https://crowdin.com/project/gitea).
|
||||
All translation work happens on [Crowdin](https://translate.gitea.com).
|
||||
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
|
||||
It is synced regularly with Crowdin. \
|
||||
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
|
||||
|
42
README.md
42
README.md
@ -9,7 +9,7 @@
|
||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
||||
[](https://crowdin.com/project/gitea "Crowdin")
|
||||
[](https://translate.gitea.com "Crowdin")
|
||||
|
||||
[View this document in Chinese](./README_ZH.md)
|
||||
|
||||
@ -31,6 +31,14 @@ For accessing free Gitea service (with a limited number of repositories), you ca
|
||||
|
||||
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/).
|
||||
|
||||
It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively.
|
||||
|
||||
If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs)
|
||||
|
||||
## Building
|
||||
|
||||
From the root of the source tree, run:
|
||||
@ -52,6 +60,8 @@ More info: https://docs.gitea.com/installation/install-from-source
|
||||
|
||||
## Using
|
||||
|
||||
After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
|
||||
|
||||
./gitea web
|
||||
|
||||
> [!NOTE]
|
||||
@ -68,22 +78,25 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## Translating
|
||||
|
||||
Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
|
||||
[](https://translate.gitea.com)
|
||||
|
||||
Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
|
||||
|
||||
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
|
||||
|
||||
https://docs.gitea.com/contributing/localization
|
||||
Get more information from [documentation](https://docs.gitea.com/contributing/localization).
|
||||
|
||||
[](https://crowdin.com/project/gitea)
|
||||
## Official and Third-Party Projects
|
||||
|
||||
## Further information
|
||||
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action.
|
||||
|
||||
For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.com/).
|
||||
If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
|
||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
|
||||
|
||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
|
||||
## Communication
|
||||
|
||||
The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
|
||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
||||
|
||||
If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
|
||||
|
||||
## Authors
|
||||
|
||||
@ -122,18 +135,25 @@ Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea"
|
||||
|
||||
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
||||
|
||||
**Where can I find the security patches?**
|
||||
|
||||
In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
||||
for the full license text.
|
||||
|
||||
## Screenshots
|
||||
## Further information
|
||||
|
||||
Looking for an overview of the interface? Check it out!
|
||||
<details>
|
||||
<summary>Looking for an overview of the interface? Check it out!</summary>
|
||||
|
||||
||||
|
||||
|:---:|:---:|:---:|
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
|
||||
</details>
|
||||
|
73
README_ZH.md
73
README_ZH.md
@ -9,13 +9,13 @@
|
||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
||||
[](https://crowdin.com/project/gitea "Crowdin")
|
||||
[](https://translate.gitea.com "Crowdin")
|
||||
|
||||
[View this document in English](./README.md)
|
||||
|
||||
## 目标
|
||||
|
||||
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86,amd64,还包括 ARM 和 PowerPC。
|
||||
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux、macOS 和 Windows 以及各种架构,除了 x86 和 amd64,还包括 ARM 和 PowerPC。
|
||||
|
||||
如果你想试用在线演示和报告问题,请访问 [demo.gitea.com](https://demo.gitea.com/)。
|
||||
|
||||
@ -23,39 +23,80 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装
|
||||
|
||||
如果你想在 Gitea Cloud 上快速部署你自己独享的 Gitea 实例,请访问 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
|
||||
|
||||
## 提示
|
||||
|
||||
1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**.
|
||||
2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。谢谢!
|
||||
3. 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||
|
||||
## 文档
|
||||
|
||||
关于如何安装请访问我们的 [文档站](https://docs.gitea.com/zh-cn/category/installation),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。
|
||||
|
||||
## 贡献流程
|
||||
## 编译
|
||||
|
||||
Fork -> Patch -> Push -> Pull Request
|
||||
在源代码的根目录下执行:
|
||||
|
||||
TAGS="bindata" make build
|
||||
|
||||
或者如果需要SQLite支持:
|
||||
|
||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
||||
|
||||
编译过程会分成2个子任务:
|
||||
|
||||
- `make backend`,需要 [Go Stable](https://go.dev/dl/),最低版本需求可查看 [go.mod](/go.mod)。
|
||||
- `make frontend`,需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
|
||||
|
||||
你需要连接网络来下载 go 和 npm modules。当从 tar 格式的源文件编译时,其中包含了预编译的前端文件,因此 `make frontend` 将不会被执行。这允许编译时不需要 Node.js。
|
||||
|
||||
更多信息: https://docs.gitea.com/installation/install-from-source
|
||||
|
||||
## 使用
|
||||
|
||||
编译之后,默认会在根目录下生成一个名为 `gitea` 的文件。你可以这样执行它:
|
||||
|
||||
./gitea web
|
||||
|
||||
> [!注意]
|
||||
> 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea)。
|
||||
|
||||
## 贡献
|
||||
|
||||
贡献流程:Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
> [!注意]
|
||||
>
|
||||
> 1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**。
|
||||
> 2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。 谢谢!
|
||||
|
||||
## 翻译
|
||||
|
||||
多语言翻译是基于Crowdin进行的.
|
||||
[](https://crowdin.com/project/gitea)
|
||||
[](https://translate.gitea.com)
|
||||
|
||||
多语言翻译是基于Crowdin进行的。
|
||||
|
||||
从 [文档](https://docs.gitea.com/contributing/localization) 中获取更多信息。
|
||||
|
||||
## 官方和第三方项目
|
||||
|
||||
Gitea 提供官方的 [go-sdk](https://gitea.com/gitea/go-sdk),以及名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具 和 用于 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
|
||||
|
||||
[gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 是一个 Gitea 相关项目的列表,你可以在这里找到更多的第三方项目,包括 SDK、插件、主题等等。
|
||||
|
||||
## 作者
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
* [Translators](options/locale/TRANSLATORS)
|
||||
- [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
- [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
- [Translators](options/locale/TRANSLATORS)
|
||||
|
||||
## 授权许可
|
||||
|
||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
|
||||
|
||||
## 截图
|
||||
## 更多信息
|
||||
|
||||
<details>
|
||||
<summary>截图</summary>
|
||||
|
||||
||||
|
||||
|:---:|:---:|:---:|
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
|
||||
</details>
|
||||
|
@ -1,4 +1,166 @@
|
||||
import {initTextareaMarkdown} from './EditorMarkdown.ts';
|
||||
import {initTextareaMarkdown, markdownHandleIndention, textareaSplitLines} from './EditorMarkdown.ts';
|
||||
|
||||
test('textareaSplitLines', () => {
|
||||
let ret = textareaSplitLines('a\nbc\nd', 0);
|
||||
expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 0, posLineIndex: 0, inlinePos: 0});
|
||||
|
||||
ret = textareaSplitLines('a\nbc\nd', 1);
|
||||
expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 0, posLineIndex: 0, inlinePos: 1});
|
||||
|
||||
ret = textareaSplitLines('a\nbc\nd', 2);
|
||||
expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 2, posLineIndex: 1, inlinePos: 0});
|
||||
|
||||
ret = textareaSplitLines('a\nbc\nd', 3);
|
||||
expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 2, posLineIndex: 1, inlinePos: 1});
|
||||
|
||||
ret = textareaSplitLines('a\nbc\nd', 4);
|
||||
expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 2, posLineIndex: 1, inlinePos: 2});
|
||||
|
||||
ret = textareaSplitLines('a\nbc\nd', 5);
|
||||
expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 5, posLineIndex: 2, inlinePos: 0});
|
||||
|
||||
ret = textareaSplitLines('a\nbc\nd', 6);
|
||||
expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 5, posLineIndex: 2, inlinePos: 1});
|
||||
});
|
||||
|
||||
test('markdownHandleIndention', () => {
|
||||
const testInput = (input: string, expected?: string) => {
|
||||
const inputPos = input.indexOf('|');
|
||||
input = input.replace('|', '');
|
||||
const ret = markdownHandleIndention({value: input, selStart: inputPos, selEnd: inputPos});
|
||||
if (expected === null) {
|
||||
expect(ret).toEqual({handled: false});
|
||||
} else {
|
||||
const expectedPos = expected.indexOf('|');
|
||||
expected = expected.replace('|', '');
|
||||
expect(ret).toEqual({
|
||||
handled: true,
|
||||
valueSelection: {value: expected, selStart: expectedPos, selEnd: expectedPos},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
testInput(`
|
||||
a|b
|
||||
`, `
|
||||
a
|
||||
|b
|
||||
`);
|
||||
|
||||
testInput(`
|
||||
1. a
|
||||
2. |
|
||||
`, `
|
||||
1. a
|
||||
|
|
||||
`);
|
||||
|
||||
testInput(`
|
||||
|1. a
|
||||
`, null); // let browser handle it
|
||||
|
||||
testInput(`
|
||||
1. a
|
||||
1. b|c
|
||||
`, `
|
||||
1. a
|
||||
2. b
|
||||
3. |c
|
||||
`);
|
||||
|
||||
testInput(`
|
||||
2. a
|
||||
2. b|
|
||||
|
||||
1. x
|
||||
1. y
|
||||
`, `
|
||||
1. a
|
||||
2. b
|
||||
3. |
|
||||
|
||||
1. x
|
||||
1. y
|
||||
`);
|
||||
|
||||
testInput(`
|
||||
2. a
|
||||
2. b
|
||||
|
||||
1. x|
|
||||
1. y
|
||||
`, `
|
||||
2. a
|
||||
2. b
|
||||
|
||||
1. x
|
||||
2. |
|
||||
3. y
|
||||
`);
|
||||
|
||||
testInput(`
|
||||
1. a
|
||||
2. b|
|
||||
3. c
|
||||
`, `
|
||||
1. a
|
||||
2. b
|
||||
3. |
|
||||
4. c
|
||||
`);
|
||||
|
||||
testInput(`
|
||||
1. a
|
||||
1. b
|
||||
2. b
|
||||
3. b
|
||||
4. b
|
||||
1. c|
|
||||
`, `
|
||||
1. a
|
||||
1. b
|
||||
2. b
|
||||
3. b
|
||||
4. b
|
||||
2. c
|
||||
3. |
|
||||
`);
|
||||
|
||||
testInput(`
|
||||
1. a
|
||||
2. a
|
||||
3. a
|
||||
4. a
|
||||
5. a
|
||||
6. a
|
||||
7. a
|
||||
8. a
|
||||
9. b|c
|
||||
`, `
|
||||
1. a
|
||||
2. a
|
||||
3. a
|
||||
4. a
|
||||
5. a
|
||||
6. a
|
||||
7. a
|
||||
8. a
|
||||
9. b
|
||||
10. |c
|
||||
`);
|
||||
|
||||
// this is a special case, it's difficult to re-format the parent level at the moment, so leave it to the future
|
||||
testInput(`
|
||||
1. a
|
||||
2. b|
|
||||
3. c
|
||||
`, `
|
||||
1. a
|
||||
1. b
|
||||
2. |
|
||||
3. c
|
||||
`);
|
||||
});
|
||||
|
||||
test('EditorMarkdown', () => {
|
||||
const textarea = document.createElement('textarea');
|
||||
@ -32,10 +194,10 @@ test('EditorMarkdown', () => {
|
||||
testInput({value: '1. \n2. ', pos: 3}, {value: '\n2. ', pos: 0});
|
||||
|
||||
testInput('- x', '- x\n- ');
|
||||
testInput('1. foo', '1. foo\n1. ');
|
||||
testInput({value: '1. a\n2. b\n3. c', pos: 4}, {value: '1. a\n1. \n2. b\n3. c', pos: 8});
|
||||
testInput('1. foo', '1. foo\n2. ');
|
||||
testInput({value: '1. a\n2. b\n3. c', pos: 4}, {value: '1. a\n2. \n3. b\n4. c', pos: 8});
|
||||
testInput('- [ ]', '- [ ]\n- ');
|
||||
testInput('- [ ] foo', '- [ ] foo\n- [ ] ');
|
||||
testInput('* [x] foo', '* [x] foo\n* [ ] ');
|
||||
testInput('1. [x] foo', '1. [x] foo\n1. [ ] ');
|
||||
testInput('1. [x] foo', '1. [x] foo\n2. [ ] ');
|
||||
});
|
||||
|
@ -14,7 +14,13 @@ export function textareaInsertText(textarea, value) {
|
||||
triggerEditorContentChanged(textarea);
|
||||
}
|
||||
|
||||
function handleIndentSelection(textarea, e) {
|
||||
type TextareaValueSelection = {
|
||||
value: string;
|
||||
selStart: number;
|
||||
selEnd: number;
|
||||
}
|
||||
|
||||
function handleIndentSelection(textarea: HTMLTextAreaElement, e) {
|
||||
const selStart = textarea.selectionStart;
|
||||
const selEnd = textarea.selectionEnd;
|
||||
if (selEnd === selStart) return; // do not process when no selection
|
||||
@ -56,53 +62,125 @@ function handleIndentSelection(textarea, e) {
|
||||
triggerEditorContentChanged(textarea);
|
||||
}
|
||||
|
||||
function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
|
||||
const selStart = textarea.selectionStart;
|
||||
const selEnd = textarea.selectionEnd;
|
||||
if (selEnd !== selStart) return; // do not process when there is a selection
|
||||
type MarkdownHandleIndentionResult = {
|
||||
handled: boolean;
|
||||
valueSelection?: TextareaValueSelection;
|
||||
}
|
||||
|
||||
const value = textarea.value;
|
||||
type TextLinesBuffer = {
|
||||
lines: string[];
|
||||
lengthBeforePosLine: number;
|
||||
posLineIndex: number;
|
||||
inlinePos: number
|
||||
}
|
||||
|
||||
// find the current line
|
||||
// * if selStart is 0, lastIndexOf(..., -1) is the same as lastIndexOf(..., 0)
|
||||
// * if lastIndexOf reruns -1, lineStart is 0 and it is still correct.
|
||||
const lineStart = value.lastIndexOf('\n', selStart - 1) + 1;
|
||||
let lineEnd = value.indexOf('\n', selStart);
|
||||
lineEnd = lineEnd < 0 ? value.length : lineEnd;
|
||||
let line = value.slice(lineStart, lineEnd);
|
||||
if (!line) return; // if the line is empty, do nothing, let the browser handle it
|
||||
export function textareaSplitLines(value: string, pos: number): TextLinesBuffer {
|
||||
const lines = value.split('\n');
|
||||
let lengthBeforePosLine = 0, inlinePos = 0, posLineIndex = 0;
|
||||
for (; posLineIndex < lines.length; posLineIndex++) {
|
||||
const lineLength = lines[posLineIndex].length + 1;
|
||||
if (lengthBeforePosLine + lineLength > pos) {
|
||||
inlinePos = pos - lengthBeforePosLine;
|
||||
break;
|
||||
}
|
||||
lengthBeforePosLine += lineLength;
|
||||
}
|
||||
return {lines, lengthBeforePosLine, posLineIndex, inlinePos};
|
||||
}
|
||||
|
||||
function markdownReformatListNumbers(linesBuf: TextLinesBuffer, indention: string) {
|
||||
const reDeeperIndention = new RegExp(`^${indention}\\s+`);
|
||||
const reSameLevel = new RegExp(`^${indention}([0-9]+)\\.`);
|
||||
let firstLineIdx: number;
|
||||
for (firstLineIdx = linesBuf.posLineIndex - 1; firstLineIdx >= 0; firstLineIdx--) {
|
||||
const line = linesBuf.lines[firstLineIdx];
|
||||
if (!reDeeperIndention.test(line) && !reSameLevel.test(line)) break;
|
||||
}
|
||||
firstLineIdx++;
|
||||
let num = 1;
|
||||
for (let i = firstLineIdx; i < linesBuf.lines.length; i++) {
|
||||
const oldLine = linesBuf.lines[i];
|
||||
const sameLevel = reSameLevel.test(oldLine);
|
||||
if (!sameLevel && !reDeeperIndention.test(oldLine)) break;
|
||||
if (sameLevel) {
|
||||
const newLine = `${indention}${num}.${oldLine.replace(reSameLevel, '')}`;
|
||||
linesBuf.lines[i] = newLine;
|
||||
num++;
|
||||
if (linesBuf.posLineIndex === i) {
|
||||
// need to correct the cursor inline position if the line length changes
|
||||
linesBuf.inlinePos += newLine.length - oldLine.length;
|
||||
linesBuf.inlinePos = Math.max(0, linesBuf.inlinePos);
|
||||
linesBuf.inlinePos = Math.min(newLine.length, linesBuf.inlinePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
recalculateLengthBeforeLine(linesBuf);
|
||||
}
|
||||
|
||||
function recalculateLengthBeforeLine(linesBuf: TextLinesBuffer) {
|
||||
linesBuf.lengthBeforePosLine = 0;
|
||||
for (let i = 0; i < linesBuf.posLineIndex; i++) {
|
||||
linesBuf.lengthBeforePosLine += linesBuf.lines[i].length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
export function markdownHandleIndention(tvs: TextareaValueSelection): MarkdownHandleIndentionResult {
|
||||
const unhandled: MarkdownHandleIndentionResult = {handled: false};
|
||||
if (tvs.selEnd !== tvs.selStart) return unhandled; // do not process when there is a selection
|
||||
|
||||
const linesBuf = textareaSplitLines(tvs.value, tvs.selStart);
|
||||
const line = linesBuf.lines[linesBuf.posLineIndex] ?? '';
|
||||
if (!line) return unhandled; // if the line is empty, do nothing, let the browser handle it
|
||||
|
||||
// parse the indention
|
||||
const indention = /^\s*/.exec(line)[0];
|
||||
line = line.slice(indention.length);
|
||||
let lineContent = line;
|
||||
const indention = /^\s*/.exec(lineContent)[0];
|
||||
lineContent = lineContent.slice(indention.length);
|
||||
if (linesBuf.inlinePos <= indention.length) return unhandled; // if cursor is at the indention, do nothing, let the browser handle it
|
||||
|
||||
// parse the prefixes: "1. ", "- ", "* ", there could also be " [ ] " or " [x] " for task lists
|
||||
// there must be a space after the prefix because none of "1.foo" / "-foo" is a list item
|
||||
const prefixMatch = /^([0-9]+\.|[-*])(\s\[([ x])\])?\s/.exec(line);
|
||||
const prefixMatch = /^([0-9]+\.|[-*])(\s\[([ x])\])?\s/.exec(lineContent);
|
||||
let prefix = '';
|
||||
if (prefixMatch) {
|
||||
prefix = prefixMatch[0];
|
||||
if (lineStart + prefix.length > selStart) prefix = ''; // do not add new line if cursor is at prefix
|
||||
if (prefix.length > linesBuf.inlinePos) prefix = ''; // do not add new line if cursor is at prefix
|
||||
}
|
||||
|
||||
line = line.slice(prefix.length);
|
||||
if (!indention && !prefix) return; // if no indention and no prefix, do nothing, let the browser handle it
|
||||
lineContent = lineContent.slice(prefix.length);
|
||||
if (!indention && !prefix) return unhandled; // if no indention and no prefix, do nothing, let the browser handle it
|
||||
|
||||
e.preventDefault();
|
||||
if (!line) {
|
||||
if (!lineContent) {
|
||||
// clear current line if we only have i.e. '1. ' and the user presses enter again to finish creating a list
|
||||
textarea.value = value.slice(0, lineStart) + value.slice(lineEnd);
|
||||
textarea.setSelectionRange(selStart - prefix.length, selStart - prefix.length);
|
||||
linesBuf.lines[linesBuf.posLineIndex] = '';
|
||||
linesBuf.inlinePos = 0;
|
||||
} else {
|
||||
// start a new line with the same indention and prefix
|
||||
// start a new line with the same indention
|
||||
let newPrefix = prefix;
|
||||
// a simple approach, otherwise it needs to parse the lines after the current line
|
||||
if (/^\d+\./.test(prefix)) newPrefix = `1. ${newPrefix.slice(newPrefix.indexOf('.') + 2)}`;
|
||||
newPrefix = newPrefix.replace('[x]', '[ ]');
|
||||
const newLine = `\n${indention}${newPrefix}`;
|
||||
textarea.value = value.slice(0, selStart) + newLine + value.slice(selEnd);
|
||||
textarea.setSelectionRange(selStart + newLine.length, selStart + newLine.length);
|
||||
|
||||
const inlinePos = linesBuf.inlinePos;
|
||||
linesBuf.lines[linesBuf.posLineIndex] = line.substring(0, inlinePos);
|
||||
const newLineLeft = `${indention}${newPrefix}`;
|
||||
const newLine = `${newLineLeft}${line.substring(inlinePos)}`;
|
||||
linesBuf.lines.splice(linesBuf.posLineIndex + 1, 0, newLine);
|
||||
linesBuf.posLineIndex++;
|
||||
linesBuf.inlinePos = newLineLeft.length;
|
||||
recalculateLengthBeforeLine(linesBuf);
|
||||
}
|
||||
|
||||
markdownReformatListNumbers(linesBuf, indention);
|
||||
const newPos = linesBuf.lengthBeforePosLine + linesBuf.inlinePos;
|
||||
return {handled: true, valueSelection: {value: linesBuf.lines.join('\n'), selStart: newPos, selEnd: newPos}};
|
||||
}
|
||||
|
||||
function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
|
||||
const ret = markdownHandleIndention({value: textarea.value, selStart: textarea.selectionStart, selEnd: textarea.selectionEnd});
|
||||
if (!ret.handled) return;
|
||||
e.preventDefault();
|
||||
textarea.value = ret.valueSelection.value;
|
||||
textarea.setSelectionRange(ret.valueSelection.selStart, ret.valueSelection.selEnd);
|
||||
triggerEditorContentChanged(textarea);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user