Skip to content

Commit 1c8bcc0

Browse files
Merge pull request #9 from wilsonneto-dev/wn/add-markdown-support
feat: Add markdown support
2 parents bcef986 + 21c715d commit 1c8bcc0

6 files changed

+104
-6
lines changed

README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
**Navigation Power-ups** is a Visual Studio Code extension that enhances your file navigation experience by adding a left bar navigation tree to files that haven't it natively. The extension is designed to support a variety of file types, offering an easy-to-use interface for jumping between sections, headers, or requests within supported files.
66

7-
Currently, the extension supports `.http` files, with plans to extend compatibility to Markdown files (`.md`) and others in the future.
7+
Currently, the extension supports `.http` and `.md` files, feel free to add any other file types you miss.
88

99
## Features
1010

@@ -16,7 +16,7 @@ Currently, the extension supports `.http` files, with plans to extend compatibil
1616
| File Type | Description | Navigation Rules |
1717
|-----------|--------------------------------------------------|----------------------------------------------------------------------------------|
1818
| `.http` | HTTP request files | Navigation based on `###` for requests. Headers with more than `###` are sections.|
19-
| `.md` | Markdown files *(Upcoming)* | Navigation based on `#`, `##`, `###`, etc. headers. |
19+
| `.md` | Markdown files | Navigation based on headers (`#`, `##`, `###`, etc). |
2020
| More | Additional file types *(Planned for future)* | TBD |
2121

2222
## Setup
@@ -29,14 +29,21 @@ Currently, the extension supports `.http` files, with plans to extend compatibil
2929

3030
4. **Real-time Updates**: As you make changes to the file (such as adding new sections, editing headers, or removing requests), the tree will update in real time to reflect these changes.
3131

32+
### Markdown Files
33+
34+
To be able to see the navigation left menu in markdown files, you just need to open the file and click in the plugin icon in the left bar.
35+
36+
![Markdown file support](image.png)
37+
38+
3239
### HTTP Files
3340

3441
In `.http` files, the extension uses the number of `#` characters to differentiate between sections and requests:
3542

3643
- **Sections**: Any headers with more than three `#` characters (e.g., `#### Section`) are treated as higher-level sections.
3744
- **Requests**: Headers with exactly three `#` characters (e.g., `### request`) are treated as individual HTTP requests.
3845

39-
### Example of `.http` file structure:
46+
#### Example of `.http` file structure:
4047

4148
![Http file example](./images/readme-httpfile-eg.png)
4249

images/readme-markdown-eg.png

315 KB
Loading

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "navigation-powerups",
33
"displayName": "Navigation Power-ups",
4-
"description": "Not a big extension, but hey, it keeps you from getting lost in the maze of .http requests. Navigate smarter, not harder :)",
5-
"version": "1.0.1",
4+
"description": "Navigate smarter, not harder :)",
5+
"version": "1.1.0",
66
"repository": "https://github.com/wilsonneto-dev/navigation-powerups",
77
"publisher": "wilsonneto-dev",
88
"icon": "images/icon.png",

src/parsers/FileToNavigationParser.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import INavigationParserStrategy from "../model/INavigationParserStrategy";
22
import HttpFileToNavParser from "./httpfiles/HttpFileToNavParser";
33
import { IFile } from "../model/IFile";
44
import NavigationNode from "../model/NavigationNode";
5+
import MarkdownFileToNavParser from "./markdown/MarkdownFileToNavParser";
56

67
const navigationParsers : { [extension: string]: INavigationParserStrategy } = {
7-
'http': new HttpFileToNavParser()
8+
'http': new HttpFileToNavParser(),
9+
'md': new MarkdownFileToNavParser()
810
};
911

1012
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import NavigationNode from "../../model/NavigationNode";
2+
import MarkdownFileToNavParser from "./MarkdownFileToNavParser";
3+
4+
const exampleMarkdown = `
5+
# Title 1
6+
## Subtitle 1.1
7+
## Subtitle 1.2
8+
9+
# Title 2
10+
## Subtitle 2.1
11+
### Subsection 2.1.1
12+
## Subtitle 2.2
13+
14+
# Title 3
15+
## Subtitle 3.1
16+
`;
17+
18+
describe('MarkdownFileToNavParser', () => {
19+
let parsedRoot: NavigationNode;
20+
21+
beforeAll(() => {
22+
const markdownParser = new MarkdownFileToNavParser();
23+
parsedRoot = markdownParser.parse(exampleMarkdown);
24+
console.log(parsedRoot);
25+
});
26+
27+
test('should parse the top-level titles properly', () => {
28+
expect(parsedRoot.getChildren().length).toBe(3);
29+
expect(parsedRoot.getChildren()[0].getName()).toBe('Title 1');
30+
expect(parsedRoot.getChildren()[1].getName()).toBe('Title 2');
31+
expect(parsedRoot.getChildren()[2].getName()).toBe('Title 3');
32+
});
33+
34+
test('should parse the subtitles properly', () => {
35+
const title1 = parsedRoot.getChildren()[0];
36+
expect(title1.getChildren().length).toBe(2);
37+
expect(title1.getChildren()[0].getName()).toBe('Subtitle 1.1');
38+
expect(title1.getChildren()[1].getName()).toBe('Subtitle 1.2');
39+
40+
const title2 = parsedRoot.getChildren()[1];
41+
expect(title2.getChildren().length).toBe(2);
42+
expect(title2.getChildren()[0].getName()).toBe('Subtitle 2.1');
43+
expect(title2.getChildren()[0].getChildren().length).toBe(1); // Subsection under Subtitle 2.1
44+
expect(title2.getChildren()[0].getChildren()[0].getName()).toBe('Subsection 2.1.1');
45+
expect(title2.getChildren()[1].getName()).toBe('Subtitle 2.2');
46+
});
47+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import INavigationParserStrategy from "../../model/INavigationParserStrategy";
2+
import NavigationNode from "../../model/NavigationNode";
3+
4+
class MarkdownFileToNavParser implements INavigationParserStrategy {
5+
public parse(text: string): NavigationNode {
6+
const root = new NavigationNode("Markdown Document");
7+
const parents: NavigationNode[] = [];
8+
parents.push(root);
9+
10+
const lines = text.split("\n");
11+
lines.forEach((line, index) => {
12+
const lineNumber = index;
13+
14+
if (this.isSectionHeader(line)) {
15+
const header = new NavigationNode(this.getName(line), this.getLevel(line), lineNumber);
16+
while (header.getLevel() <= parents.at(-1)!.getLevel()) {
17+
parents.pop();
18+
}
19+
20+
parents.at(-1)!.addChild(header);
21+
parents.push(header);
22+
}
23+
});
24+
25+
return root;
26+
}
27+
28+
private isSectionHeader(line: string): boolean {
29+
return line.trim().startsWith("#") && line.trim().split(" ").length > 1;
30+
}
31+
32+
private getName(line: string): string {
33+
return line.trim().split(" ").slice(1).join(" ").trim();
34+
}
35+
36+
private getLevel(line: string): number {
37+
const numberOfSharps = line.trim().split(" ")[0].split('#').length - 1;
38+
return numberOfSharps; // Levels are 0-based for easier hierarchy
39+
}
40+
}
41+
42+
export default MarkdownFileToNavParser;

0 commit comments

Comments
 (0)