Skip to content

Commit e7c7241

Browse files
sebthommickaelistria
authored andcommitted
Add Markdown Language Server support
1 parent ef2a4e7 commit e7c7241

File tree

17 files changed

+1902
-27
lines changed

17 files changed

+1902
-27
lines changed

org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/AllCleanRule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,12 @@ public static void closeIntro() {
7474
public static void enableLogging() {
7575
ScopedPreferenceStore prefs = new ScopedPreferenceStore(InstanceScope.INSTANCE, "org.eclipse.lsp4e");
7676
prefs.putValue("org.eclipse.wildwebdeveloper.angular.file.logging.enabled", Boolean.toString(true));
77+
prefs.putValue("org.eclipse.wildwebdeveloper.astro.file.logging.enabled", Boolean.toString(true));
7778
prefs.putValue("org.eclipse.wildwebdeveloper.jsts.file.logging.enabled", Boolean.toString(true));
7879
prefs.putValue("org.eclipse.wildwebdeveloper.css.file.logging.enabled", Boolean.toString(true));
7980
prefs.putValue("org.eclipse.wildwebdeveloper.html.file.logging.enabled", Boolean.toString(true));
8081
prefs.putValue("org.eclipse.wildwebdeveloper.json.file.logging.enabled", Boolean.toString(true));
82+
prefs.putValue("org.eclipse.wildwebdeveloper.markdown.file.logging.enabled", Boolean.toString(true));
8183
prefs.putValue("org.eclipse.wildwebdeveloper.xml.file.logging.enabled", Boolean.toString(true));
8284
prefs.putValue("org.eclipse.wildwebdeveloper.yaml.file.logging.enabled", Boolean.toString(true));
8385
prefs.putValue("org.eclipse.wildwebdeveloper.eslint.file.logging.enabled", Boolean.toString(true));
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Vegard IT GmbH and others.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Sebastian Thomschke (Vegard IT GmbH) - initial implementation
12+
*******************************************************************************/
13+
package org.eclipse.wildwebdeveloper.tests;
14+
15+
import static org.eclipse.core.resources.IMarker.*;
16+
import static org.eclipse.wildwebdeveloper.markdown.MarkdownDiagnosticsManager.MARKDOWN_MARKER_TYPE;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
19+
import java.nio.charset.StandardCharsets;
20+
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.concurrent.atomic.AtomicReference;
23+
import java.util.stream.Collectors;
24+
25+
import org.eclipse.core.resources.IFile;
26+
import org.eclipse.core.resources.IMarker;
27+
import org.eclipse.core.resources.IResource;
28+
import org.eclipse.core.resources.ResourcesPlugin;
29+
import org.eclipse.core.runtime.CoreException;
30+
import org.eclipse.lsp4e.LSPEclipseUtils;
31+
import org.eclipse.lsp4e.LanguageServerWrapper;
32+
import org.eclipse.lsp4e.LanguageServiceAccessor;
33+
import org.eclipse.ui.PlatformUI;
34+
import org.eclipse.ui.editors.text.TextEditor;
35+
import org.eclipse.ui.ide.IDE;
36+
import org.eclipse.ui.tests.harness.util.DisplayHelper;
37+
import org.junit.jupiter.api.Test;
38+
import org.junit.jupiter.api.extension.ExtendWith;
39+
40+
record MarkdownTest(String markdown, String messagePattern, int severity) {
41+
}
42+
43+
@ExtendWith(AllCleanRule.class)
44+
class TestMarkdown {
45+
46+
@Test
47+
void diagnosticsCoverTypicalMarkdownIssues() throws Exception {
48+
var project = ResourcesPlugin.getWorkspace().getRoot().getProject(getClass().getName() + System.nanoTime());
49+
project.create(null);
50+
project.open(null);
51+
52+
final var markerTests = Collections.synchronizedCollection(new ArrayList<MarkdownTest>());
53+
markerTests
54+
.add(new MarkdownTest("Reference link to [an undefined reference][missing-ref]", "No link definition found: 'missing-ref'",
55+
SEVERITY_WARNING));
56+
markerTests.add(
57+
new MarkdownTest("Relative file link: [data](./nonexistent-folder/data.csv)", "File does not exist at path: .*data\\.csv",
58+
SEVERITY_WARNING));
59+
markerTests.add(new MarkdownTest("Broken image: ![logo](../assets/logo.png)", "File does not exist at path: .*logo\\.png",
60+
SEVERITY_WARNING));
61+
markerTests.add(new MarkdownTest("Link to missing header in this file: [Jump to Setup](#setup)", "No header found: 'setup'",
62+
SEVERITY_WARNING));
63+
markerTests.add(new MarkdownTest("Link to missing header in another file: [See Guide](./GUIDE.md#installing)",
64+
"Header does not exist in file: installing",
65+
SEVERITY_WARNING));
66+
markerTests.add(new MarkdownTest("Undefined footnote here [^missing-footnote]", "No link definition found: '\\^missing-footnote'",
67+
SEVERITY_WARNING));
68+
markerTests.add(new MarkdownTest("This is a paragraph with an [undefined link][undefined-link].",
69+
"No link definition found: 'undefined-link'",
70+
SEVERITY_WARNING));
71+
markerTests.add(new MarkdownTest("[unused-link]: https://unused-link.com", "Link definition is unused",
72+
SEVERITY_WARNING));
73+
markerTests.add(new MarkdownTest("""
74+
This is a paragraph with a [duplicate link][duplicate-link].
75+
[duplicate-link]: https://duplicate-link.com
76+
[duplicate-link]: https://duplicate-link.com
77+
""", "Link definition for 'duplicate-link' already exists", SEVERITY_ERROR));
78+
79+
final IFile referencedFile = project.getFile("GUIDE.md");
80+
referencedFile.create("".getBytes(), true, false, null);
81+
82+
final IFile file = project.getFile("broken.md");
83+
file.create(markerTests.stream().map(MarkdownTest::markdown).collect(Collectors.joining("\n")).getBytes(StandardCharsets.UTF_8),
84+
true,
85+
false, null);
86+
87+
final var editor = (TextEditor) IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
88+
final var display = editor.getSite().getShell().getDisplay();
89+
final var doc = editor.getDocumentProvider().getDocument(editor.getEditorInput());
90+
91+
/*
92+
* ensure Markdown Language Server is started and connected
93+
*/
94+
final var markdownLS = new AtomicReference<LanguageServerWrapper>();
95+
DisplayHelper.waitForCondition(display, 10_000, () -> {
96+
markdownLS.set(LanguageServiceAccessor.getStartedWrappers(doc, null, false).stream() //
97+
.filter(w -> "org.eclipse.wildwebdeveloper.markdown".equals(w.serverDefinition.id)) //
98+
.findFirst().orElse(null));
99+
return markdownLS.get() != null //
100+
&& markdownLS.get().isActive() //
101+
&& markdownLS.get().isConnectedTo(LSPEclipseUtils.toUri(doc));
102+
});
103+
104+
// Wait until all expected diagnostics are present (by message fragments)
105+
DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 15_000, () -> {
106+
try {
107+
final var markers = file.findMarkers(MARKDOWN_MARKER_TYPE, true, IResource.DEPTH_ZERO);
108+
if (markers.length == 0)
109+
return false;
110+
111+
for (final IMarker m : markers) {
112+
final Object msgObj = m.getAttribute(IMarker.MESSAGE);
113+
if (!(msgObj instanceof final String msg))
114+
continue;
115+
markerTests.removeIf(t -> t.severity() == m.getAttribute(IMarker.SEVERITY, -1) &&
116+
msg.matches(t.messagePattern()));
117+
}
118+
return markerTests.isEmpty();
119+
} catch (CoreException e) {
120+
return false;
121+
}
122+
});
123+
124+
assertTrue(markerTests.isEmpty(), "The following markers were not found: " + markerTests);
125+
}
126+
}

org.eclipse.wildwebdeveloper/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ Bundle-ActivationPolicy: lazy
3737
Eclipse-BundleShape: dir
3838
Export-Package: org.eclipse.wildwebdeveloper.debug;x-internal:=true,
3939
org.eclipse.wildwebdeveloper.debug.node;x-internal:=true,
40-
org.eclipse.wildwebdeveloper.debug.npm;x-internal:=true
40+
org.eclipse.wildwebdeveloper.debug.npm;x-internal:=true,
41+
org.eclipse.wildwebdeveloper.markdown;x-friends:="org.eclipse.wildwebdeveloper.tests"

org.eclipse.wildwebdeveloper/package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"dependencies": {
33
"@angular/language-server": "20.3.0",
4-
"astro-vscode" : "2.16.0",
4+
"astro-vscode": "2.16.0",
55
"firefox-debugadapter": "2.15.0",
66
"typescript": "5.9.3",
77
"typescript-language-server": "5.0.1",
@@ -11,14 +11,18 @@
1111
"vscode-css-languageservice": "6.3.8",
1212
"vscode-html-languageservice": "5.6.0",
1313
"vscode-json-languageservice": "5.6.2",
14-
"@vue/language-server" : "3.1.2",
15-
"@vue/typescript-plugin" : "3.1.2",
16-
"fsevents" : "2.3.3",
14+
"vscode-markdown-languageserver": "^0.5.0-alpha.12",
15+
"markdown-it": "14.1.0",
16+
"@vue/language-server": "3.1.2",
17+
"@vue/typescript-plugin": "3.1.2",
1718
"vscode-css-languageserver": "file:target/vscode-css-languageserver-1.0.0.tgz",
1819
"vscode-html-languageserver": "file:target/vscode-html-languageserver-1.0.0.tgz",
1920
"vscode-json-languageserver": "file:target/vscode-json-languageserver-1.3.4.tgz",
2021
"eslint-server": "file:target/eslint-server-2.4.1.tgz"
2122
},
23+
"optionalDependencies": {
24+
"fsevents": "2.3.3"
25+
},
2226
"overrides": {
2327
"vscode-languageserver-types": "3.17.6-next.6"
2428
}

org.eclipse.wildwebdeveloper/plugin.properties

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ TypeScriptInlayHintPreferencePage.name=Inlay Hint
4545
JavaScriptPreferencePage.name=JavaScript
4646
JavaScriptInlayHintPreferencePage.name=Inlay Hint
4747

48+
# Markdown
49+
MarkdownPreferencePage.name=Markdown (Wild Web Developer)
50+
MarkdownProblem=Markdown Problem
51+
4852
# YAML
4953
YAMLPreferencePage.name=YAML (Wild Web Developer)
5054
YAMLCompletionPreferencePage.name=Completion
@@ -62,4 +66,5 @@ preferenceKeywords.css=css
6266
preferenceKeywords.less=less
6367
preferenceKeywords.scss=scss
6468
preferenceKeywords.sass=sass
65-
preferenceKeywords.html=html
69+
preferenceKeywords.html=html
70+
preferenceKeywords.markdown=markdown

org.eclipse.wildwebdeveloper/plugin.xml

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
priority="low">
116116
</content-type>
117117
</extension>
118-
118+
119119
<extension
120120
point="org.eclipse.lsp4e.languageServer">
121121
<server
@@ -131,8 +131,39 @@
131131
</contentTypeMapping>
132132
</extension>
133133

134+
<!-- Markdown Language -->
135+
<extension point="org.eclipse.lsp4e.languageServer">
136+
<server id="org.eclipse.wildwebdeveloper.markdown"
137+
class="org.eclipse.wildwebdeveloper.markdown.MarkdownLanguageServer"
138+
clientImpl="org.eclipse.wildwebdeveloper.markdown.MarkdownLanguageClient"
139+
serverInterface="org.eclipse.wildwebdeveloper.markdown.MarkdownLanguageServerAPI"
140+
label="Markdown Language Server" />
141+
<contentTypeMapping
142+
contentType="org.eclipse.tm4e.language_pack.markdown"
143+
id="org.eclipse.wildwebdeveloper.markdown"
144+
languageId="markdown" />
145+
</extension>
146+
147+
<extension point="org.eclipse.ui.preferencePages">
148+
<page id="org.eclipse.wildwebdeveloper.markdown.ui.preferences.MarkdownPreferencePage"
149+
class="org.eclipse.wildwebdeveloper.markdown.ui.preferences.MarkdownPreferencePage"
150+
name="%MarkdownPreferencePage.name">
151+
<keywordReference id="org.eclipse.wildwebdeveloper.markdown" />
152+
</page>
153+
</extension>
154+
<extension point="org.eclipse.core.runtime.preferences"
155+
id="MarkdownPreferenceInitializer"
156+
name="MarkdownPreferenceInitializer">
157+
<initializer class="org.eclipse.wildwebdeveloper.markdown.ui.preferences.MarkdownPreferenceInitializer" />
158+
</extension>
159+
160+
<extension id="org.eclipse.wildwebdeveloper.markdown.problem" name="%MarkdownProblem" point="org.eclipse.core.resources.markers">
161+
<!-- Markdown problem marker type -->
162+
<super type="org.eclipse.core.resources.problemmarker"/>
163+
<persistent value="true"/>
164+
</extension>
165+
134166
<!-- CSS Language -->
135-
136167
<extension
137168
point="org.eclipse.lsp4e.languageServer">
138169
<server
@@ -158,7 +189,7 @@
158189
languageId="less">
159190
</contentTypeMapping>
160191
</extension>
161-
192+
162193
<extension point="org.eclipse.ui.genericeditor.icons">
163194
<icon contentType="org.eclipse.tm4e.language_pack.css" icon="icons/cssEditorIcon.png"/>
164195
</extension>
@@ -178,7 +209,7 @@
178209
name="%CSSCompletionPreferencePage.name">
179210
<keywordReference id="org.eclipse.wildwebdeveloper.css" />
180211
</page>
181-
<!-- Once https://github.com/microsoft/vscode/issues/164772 will be fixed, please uncomment this CSS format preference page to benefit with CSS formatting
212+
<!-- Once https://github.com/microsoft/vscode/issues/164772 will be fixed, please uncomment this CSS format preference page to benefit with CSS formatting
182213
<page
183214
category="org.eclipse.wildwebdeveloper.css.ui.preferences.CSSPreferencePage"
184215
class="org.eclipse.wildwebdeveloper.css.ui.preferences.CSSFormatPreferencePage"
@@ -192,7 +223,7 @@
192223
id="org.eclipse.wildwebdeveloper.css.ui.preferences.CSSHoverPreferencePage"
193224
name="%CSSHoverPreferencePage.name">
194225
<keywordReference id="org.eclipse.wildwebdeveloper.css" />
195-
</page>
226+
</page>
196227
<page
197228
category="org.eclipse.wildwebdeveloper.css.ui.preferences.CSSPreferencePage"
198229
class="org.eclipse.wildwebdeveloper.css.ui.preferences.CSSValidationPreferencePage"
@@ -218,7 +249,7 @@
218249
name="%LESSCompletionPreferencePage.name">
219250
<keywordReference id="org.eclipse.wildwebdeveloper.less" />
220251
</page>
221-
<!-- Once https://github.com/microsoft/vscode/issues/164772 will be fixed, please uncomment this LESS format preference page to benefit with LESS formatting
252+
<!-- Once https://github.com/microsoft/vscode/issues/164772 will be fixed, please uncomment this LESS format preference page to benefit with LESS formatting
222253
<page
223254
category="org.eclipse.wildwebdeveloper.css.ui.preferences.less.LESSPreferencePage"
224255
class="org.eclipse.wildwebdeveloper.css.ui.preferences.less.LESSFormatPreferencePage"
@@ -232,7 +263,7 @@
232263
id="org.eclipse.wildwebdeveloper.css.ui.preferences.less.LESSHoverPreferencePage"
233264
name="%LESSHoverPreferencePage.name">
234265
<keywordReference id="org.eclipse.wildwebdeveloper.less" />
235-
</page>
266+
</page>
236267
<page
237268
category="org.eclipse.wildwebdeveloper.css.ui.preferences.less.LESSPreferencePage"
238269
class="org.eclipse.wildwebdeveloper.css.ui.preferences.less.LESSValidationPreferencePage"
@@ -260,7 +291,7 @@
260291
<keywordReference id="org.eclipse.wildwebdeveloper.scss" />
261292
<keywordReference id="org.eclipse.wildwebdeveloper.sass" />
262293
</page>
263-
<!-- Once https://github.com/microsoft/vscode/issues/164772 will be fixed, please uncomment this SCSS format preference page to benefit with SCSS formatting
294+
<!-- Once https://github.com/microsoft/vscode/issues/164772 will be fixed, please uncomment this SCSS format preference page to benefit with SCSS formatting
264295
<page
265296
category="org.eclipse.wildwebdeveloper.css.ui.preferences.scss.SCSSPreferencePage"
266297
class="org.eclipse.wildwebdeveloper.css.ui.preferences.scss.SCSSFormatPreferencePage"
@@ -275,7 +306,7 @@
275306
name="%SCSSHoverPreferencePage.name">
276307
<keywordReference id="org.eclipse.wildwebdeveloper.scss" />
277308
<keywordReference id="org.eclipse.wildwebdeveloper.sass" />
278-
</page>
309+
</page>
279310
<page
280311
category="org.eclipse.wildwebdeveloper.css.ui.preferences.scss.SCSSPreferencePage"
281312
class="org.eclipse.wildwebdeveloper.css.ui.preferences.scss.SCSSValidationPreferencePage"
@@ -295,7 +326,7 @@
295326
</initializer>
296327
</extension>
297328

298-
<!-- HTML Language -->
329+
<!-- HTML Language -->
299330
<extension
300331
point="org.eclipse.lsp4e.languageServer">
301332
<server
@@ -352,7 +383,7 @@
352383
id="org.eclipse.wildwebdeveloper.html.ui.preferences.HTMLHoverPreferencePage"
353384
name="%HTMLHoverPreferencePage.name">
354385
<keywordReference id="org.eclipse.wildwebdeveloper.html" />
355-
</page>
386+
</page>
356387
<page
357388
category="org.eclipse.wildwebdeveloper.html.ui.preferences.HTMLPreferencePage"
358389
class="org.eclipse.wildwebdeveloper.html.ui.preferences.HTMLValidationPreferencePage"
@@ -371,7 +402,7 @@
371402
</initializer>
372403
</extension>
373404

374-
<!-- JavaScript/TypeScript Language -->
405+
<!-- JavaScript/TypeScript Language -->
375406
<extension
376407
point="org.eclipse.lsp4e.languageServer">
377408
<server
@@ -428,7 +459,7 @@
428459
contentType="org.eclipse.tm4e.language_pack.typescriptreact"
429460
id="org.eclipse.wildwebdeveloper.eslint"/>
430461
</extension>
431-
462+
432463
<extension point="org.eclipse.ui.genericeditor.icons">
433464
<icon contentType="org.eclipse.tm4e.language_pack.javascript" icon="icons/jsEditorIcon.png"/>
434465
<icon contentType="org.eclipse.tm4e.language_pack.javascriptreact" icon="icons/jsEditorIcon.png"/>
@@ -490,7 +521,7 @@
490521
name="%JavaScriptInlayHintPreferencePage.name">
491522
<keywordReference id="org.eclipse.wildwebdeveloper.js" />
492523
<keywordReference id="org.eclipse.wildwebdeveloper.javascript" />
493-
</page>
524+
</page>
494525
<!-- TypeScript -->
495526
<page
496527
category="org.eclipse.wildwebdeveloper.jsts.ui.preferences.JSTSPreferencePage"
@@ -572,6 +603,7 @@
572603
label="VUE Language Server"/>
573604
<contentTypeMapping contentType="org.eclipse.wildwebdeveloper.vue" languageId="vue" id="org.eclipse.wildwebdeveloper.vue"/>
574605
</extension>
606+
575607
<extension point="org.eclipse.tm4e.registry.grammars">
576608
<grammar path="grammars/vue/vue.tmLanguage.json" scopeName="source.vue" />
577609
<scopeNameContentTypeBinding contentTypeId="org.eclipse.wildwebdeveloper.vue" scopeName="source.vue" />
@@ -604,9 +636,8 @@
604636
contentTypeId="org.eclipse.wildwebdeveloper.vue"
605637
path="language-configurations/vue/vue-language-configuration.json">
606638
</languageConfiguration>
607-
608639
</extension>
609-
640+
610641
<extension
611642
point="org.eclipse.ui.genericeditor.reconcilers">
612643
<reconciler
@@ -615,7 +646,7 @@
615646
</reconciler>
616647
</extension>
617648

618-
<!-- YAML Language -->
649+
<!-- YAML Language -->
619650
<extension
620651
point="org.eclipse.lsp4e.languageServer">
621652
<server
@@ -663,7 +694,7 @@
663694
id="org.eclipse.wildwebdeveloper.yaml.ui.preferences.YAMLHoverPreferencePage"
664695
name="%YAMLHoverPreferencePage.name">
665696
<keywordReference id="org.eclipse.wildwebdeveloper.yaml" />
666-
</page>
697+
</page>
667698
<page
668699
category="org.eclipse.wildwebdeveloper.yaml.ui.preferences.YAMLPreferencePage"
669700
class="org.eclipse.wildwebdeveloper.yaml.ui.preferences.YAMLValidationPreferencePage"

0 commit comments

Comments
 (0)