mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-08 09:38:26 -05:00
Merge vscode source through release 1.79.2 (#23482)
* log when an editor action doesn't run because of enablement * notebooks create/dispose editors. this means controllers must be created eagerly (😢) and that notebooks need a custom way of plugging comparision keys for session. works unless creating another session for the same cell of a duplicated editor * Set offSide to sql lang configuration to true (#183461) * Fixes #181764 (#183550) * fix typo * Always scroll down and focus the input (#183557) * Fixes #180386 (#183561) * cli: ensure ordering of rpc server messages (#183558) * cli: ensure ordering of rpc server messages Sending lots of messages to a stream would block them around the async tokio mutex, which is "fair" so doesn't preserve ordering. Instead, use the write_loop approach I introduced to the server_multiplexer for the same reason some time ago. * fix clippy * update for May endgame * testing: allow invalidateTestResults to take an array (#183569) * Document `ShareProvider` API proposal (#183568) * Document `ShareProvider` API proposal * Remove mention of VS Code from JSDoc * Add support for rendering svg and md in welcome message (#183580) * Remove toggle setting more eagerly (#183584) * rm message abt macOS * Change text (#183589) * Change text * Accidentally changed the wrong file * cli: improve output for code tunnel status (#183571) * testing: allow invalidateTestResults to take an array * cli: improve output for code tunnel status Fixes #183570 * [json/css/html] update services (#183595) * Add experimental setting to enable this dialog * Fix exporting chat model to JSON before it is initialized (#183597) * minimum scrolling to reveal the next cell on shift+enter (#183600) do minimum scrolling to reveal the next cell on Execute cell and select next * Fixing Jupyter notebook issue 13263 (#183527) fix for the issue, still need to understand why there is strange focusing * Tweak proposed API JSDoc (#183590) * Tweak proposed API JSDoc * workbench -> workspace * fix ? operator * Use active editor and show progress when sharing (#183603) Use active editor and show progress * use scroll setting variable correctly * Schedule welcome widget to show once between typing. (#183606) * Schedule dialog to show once between typing * Don't re-render if already displayed once * Add F10 keybinding for debugger step, even on Web. (#183510) Fixes #181792. Previously, for Web the keyboard shortcut was Alt-F10, because it was believed that F10 could not be bound on browsers. This turned out to be incorrect, so we make the shortcut consistent (F10) with desktop VSCode which is also what many other debuggers use. We keep Alt-F10 on web as a secondary keybinding to keep the experience some web users may have gotten used to by now. * Also pass process.env * Restore missing chat clear commands (#183651) * chore: update electron@22.5.4 (#183716) * Show remote indicator in web when remoteAuthority is set (#183728) * feat: .vuerc as json file (#153017) Co-authored-by: Martin Aeschlimann <martinae@microsoft.com> * Delete --compatibility=1.63 code from the server (#183738) * Copy vscode.dev link to tunnel generates an invalid link when an untitled workspace is open (#183739) * Recent devcontainer display string corrupted on Get Started page (#183740) * Improve "next codeblock" navigation (#183744) * Improve "next codeblock" navigation Operate on the current focused response, or the last one, and scroll to the selected item * Normalize command title * Git - run git status if similarityThreshold changes (#183762) * fix aria-label issue in kb editor fixes A11y_GradeB_VSCode_Keyboard shortcut reads words together - Blind: Arrow key navigation to row Find the binding keys and "when" cell data are read together resulting in a word " CTRL + FeditorFocus instead of CTRL + F editorFocus" #182490 * Status - fix compact padding (#183768) * Remove angle brackets from VB brackets (#183782) Fixes #183359 * Update language config schema with more details about brackets. (#183779) * fix comment (#183812) * Support for `Notebook` CodeAction Kind (#183457) * nb kind support -- wip * allow notebook codeactions around single cell edit check * move notebook code action type out of editor --------- Co-authored-by: rebornix <penn.lv@gmail.com> * cli: fix connection default being applied (#183827) * cli: bump to openssl 1.1.1u (#183828) * Implement "delete" action for chat history (#183609) * Use desired file name when generating new md pasted file paths (#183861) Fixes #183851 * Default to filename for markdown new file if empty (#183864) Fixes #183848 * Fix small typo (#183865) Fixes #183819 * Noop when moving a symbol into the file it is already in (#183866) Fixes #183793 * Adjust codeAction validation to account for notebook kind (#183859) * Make JS/TS `go to configuration` commands work on non-`file:` file systems (#183688) Make `go to project` commands work on non-`file:` file systems Fixes #183685 * Can't do regex search after opening notebook (#183884) Fixes #183858 * Default to current dir for `move to file` select (#183875) Fixes #183870 `showOpenDialog` seems to ignore `defaultUri` if the file doesn't exist * Use `<...>` style markdown links when needed (#183876) Fixes #183849 * Remove check for context keys * Update xterm package * Enable updating a chat model without triggering incremental typing (#183894) * Enable chat "move" commands on empty sessions (#183895) * Enable chat "move" commands on empty sessions and also imported sessions * Fix command name * Fix some chat keybindings on windows (#183896) * "Revert File" on inactive editors are ignored (fix #177557) (#183903) * Empty reason while switching profile (fix #183775) (#183904) * fix https://github.com/microsoft/vscode-internalbacklog/issues/4278 (#183910) * fix https://github.com/microsoft/vscode/issues/183770 (#183914) * code --status displays a lot of errors before actual status output (fix #183787) (#183915) * joh/icy manatee (#183917) * Use idle value for widget of interactive editor controller https://github.com/microsoft/vscode/issues/183820 * also make preview editors idle values https://github.com/microsoft/vscode/issues/183820 * Fix #183777 (#183929) * Fix #182309 (#183925) * Tree checkbox item -> items (#183931) Fixes #183826 * Fixes #183909 (#183940) * Fix #183837 (#183943) fix #183837 * Git - fix #183941 (#183944) * Update xterm.css Fixes #181242 * chore: add @ulugbekna and @aiday-mar to my-endgame notebook (#183946) * Revert "When snippet mode is active, make `Tab` not accept suggestion but advance placeholder" This reverts commit 50a80cdb61511343996ff1d41d0b676c3d329f48. * revert not focusing completion list when quick suggest happens during snippet * change `snippetsPreventQuickSuggestions` default to false * Fix #181446 (#183956) * fix https://github.com/microsoft/vscode-internalbacklog/issues/4298 (#183957) * fix: remove extraneous incorrect context keys (#183959) These were actually getting added in getTestItemContextOverlay, and the test ID was using the extended ID which extensions do not know about. Fixes #183612 * Fixes https://github.com/microsoft/monaco-editor/issues/3920 (#183960) * fix https://github.com/microsoft/vscode-internalbacklog/issues/4324 (#183961) * fix #183030 * fix #180826 (#183962) * make message more generic for interactive editor help * . * fix #183968 * Keep codeblock toolbar visible when focused * Fix when clause on "Run in terminal" command * add important info to help menu * fix #183970 * Set `isRefactoring` for all TS refactoring edits (#183982) * consolidate * Disable move to file in TS versions < 5.2 (#183992) There are still a few key bugs with refactoring. We will ship this as a preview for TS 5.2+ instead of for 5.1 * Polish query accepting (#183995) We shouldn't send the same request to Copilot if the query hasn't changed. So if the query is the same, we short circut. Fixes https://github.com/microsoft/vscode-internalbacklog/issues/4286 Also, when we open in chat, we should use the last accepted query, not what's in the input box. Fixes https://github.com/microsoft/vscode-internalbacklog/issues/4280 * Allow widget to have focus (#184000) So that selecting non-code text works. Fixes https://github.com/microsoft/vscode-internalbacklog/issues/4294 * Fix microsoft/vscode-internalbacklog#4257. Mitigate zindex for zone widgets. (#184001) * Change welcome dialog contribution to Eventually * Misc fixes * Workspace folder picker entry descriptions are suboptimal for some filesystems (fix #183418) (#184018) * cli - ignore std error unless verbose (#183787) (#184031) * joh/inquisitive meerkat (#184034) * only stash sessions that are none empty https://github.com/microsoft/vscode-internalbacklog/issues/4281 * only unstash a session once - unless new exchanges are made, https://github.com/microsoft/vscode-internalbacklog/issues/4281 * account for all exchange types * Improve declared components (#184039) * make sure to read setting (#184040) d'oh, related to https://github.com/microsoft/vscode/issues/173387#issuecomment-1571696644 * [html] update service (#184049) [html] update service. FIxes #181176 * reset context keys on reset/hide (#184042) fixes https://github.com/microsoft/vscode-internalbacklog/issues/4330 * use `Lazy`, not `IdleValue` for the IE widget held by the eager controller (#184048) https://github.com/microsoft/vscode/issues/183820 * fix https://github.com/microsoft/vscode-internalbacklog/issues/4333 (#184067) * use undo-loop instead of undo-edit when discarding chat session (#184063) * use undo-loop instead of undo-edit when discarding chat session fixes https://github.com/microsoft/vscode-internalbacklog/issues/4118 * fix tests, wait for correct state * Add logging to node download (#184070) Add logging to node download. For #182951 * re-enable default zone widget revealing when showing (#184072) fixes https://github.com/microsoft/vscode-internalbacklog/issues/4332, also fixes https://github.com/microsoft/vscode-internalbacklog/issues/3784 * fix #178202 * Allow APIs in stable (#184062) * Fix microsoft/vscode-internalbacklog#4206. Override List view whitespace css for monaco editor (#184087) * Fix JSDoc grammatical error (#184090) * Pick up TS 5.1.3 (#184091) Fixes #182931 * Misc fixes * update distro (#184097) * chore: update electron@22.5.5 (#184116) * Extension host veto is registered multiple times on restart (fix #183778) (#184127) Extension host veto is registered multiple times on restart (#183778) * Do not auto start the local web worker extension host (#184137) * Allow embedders to intercept trustedTypes.createPolicy calls (#184136) Allow embedders to intercept trustedTypes.createPolicy calls (#184100) * fix: reading from console output for --status on windows and linux (#184138) fix: reading from console output for --status on windows and linux (#184118) * Misc fixes * code --status displays a lot of errors before actual status output (fix #183787) (#184200) fix 183787 * (cherry-pick to 1.79 from main) Handle galleryExtension failure in featuredExtensionService (#184205) Handle galleryExtension failure in featuredExtensionService (#184198) Handle galleryExtension failure * Fix #184183. Multiple output height updates are skipped. (#184188) * Post merge init fixes * Misc build issues * disable toggle inline diff of `alt` down https://github.com/microsoft/vscode-internalbacklog/issues/4342 * Take into account already activated extensions when computing running locations (#184303) Take into account already activated extensions when computing running locations (fixes #184180) * Avoid `extensionService.getExtension` and use `ActivationKind.Immediate` to allow that URI handling works while resolving (#184310) Avoid `extensionService.getExtension` and use `ActivationKind.Immediate` to allow that URI handling works while resolving (fixes #182217) * WIP * rm fish auto injection * More breaks * Fix Port Attributes constructor (#184412) * WIP * WIP * Allow extensions to get at the exports of other extensions during resolving (#184487) Allow extensions to get at the exports of other extensions during resolving (fixes #184472) * do not auto finish session when inline chat widgets have focus re https://github.com/microsoft/vscode-internalbacklog/issues/4354 * fix compile errors caused by new base method * WIP * WIP * WIP * WIP * Build errors * unc - fix path traversal bypass * Bump version * cherry-pick prod changes from main * Disable sandbox * Build break from merge * bump version * Merge pull request #184739 from max06/max06/issue184659 Restore ShellIntegration for fish (#184659) * Git - only add --find-renames if the value is not the default one (#185053) Git - only add --find-renames if the value is not the default one (#184992) * Cherry-pick: Revert changes to render featured extensions when available (#184747) Revert changes to render featured extensions when available. (#184573) * Lower timeouts for experimentation and gallery service * Revert changes to render extensions when available * Add audio cues * fix: disable app sandbox when --no-sandbox is present (#184913) * fix: disable app sandbox when --no-sandbox is present (#184897) * fix: loading minimist in packaged builds * Runtime errors * UNC allow list checks cannot be disabled in extension host (fix #184989) (#185085) * UNC allow list checks cannot be disabled in extension host (#184989) * Update src/vs/base/node/unc.js Co-authored-by: Robo <hop2deep@gmail.com> --------- Co-authored-by: Robo <hop2deep@gmail.com> * Add notebook extension * Fix mangling issues * Fix mangling issues * npm install * npm install * Issues blocking bundle * Fix build folder compile errors * Fix windows bundle build * Linting fixes * Fix sqllint issues * Update yarn.lock files * Fix unit tests * Fix a couple breaks from test fixes * Bump distro * redo the checkbox style * Update linux build container dockerfile * Bump build image tag * Bump native watch dog package * Bump node-pty * Bump distro * Fix documnetation error * Update distro * redo the button styles * Update datasource TS * Add missing yarn.lock files * Windows setup fix * Turn off extension unit tests while investigating * color box style * Remove appx * Turn off test log upload * update dropdownlist style * fix universal app build error (#23488) * Skip flaky bufferContext vscode test --------- Co-authored-by: Johannes <johannes.rieken@gmail.com> Co-authored-by: Henning Dieterichs <hdieterichs@microsoft.com> Co-authored-by: Julien Richard <jairbubbles@hotmail.com> Co-authored-by: Charles Gagnon <chgagnon@microsoft.com> Co-authored-by: Megan Rogge <merogge@microsoft.com> Co-authored-by: meganrogge <megan.rogge@microsoft.com> Co-authored-by: Rob Lourens <roblourens@gmail.com> Co-authored-by: Connor Peet <connor@peet.io> Co-authored-by: Joyce Er <joyce.er@microsoft.com> Co-authored-by: Bhavya U <bhavyau@microsoft.com> Co-authored-by: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Co-authored-by: Martin Aeschlimann <martinae@microsoft.com> Co-authored-by: Aaron Munger <aamunger@microsoft.com> Co-authored-by: Aiday Marlen Kyzy <amarlenkyzy@microsoft.com> Co-authored-by: rebornix <penn.lv@gmail.com> Co-authored-by: Ole <oler@google.com> Co-authored-by: Jean Pierre <jeanp413@hotmail.com> Co-authored-by: Robo <hop2deep@gmail.com> Co-authored-by: Yash Singh <saiansh2525@gmail.com> Co-authored-by: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Co-authored-by: Ulugbek Abdullaev <ulugbekna@gmail.com> Co-authored-by: Alex Ross <alros@microsoft.com> Co-authored-by: Michael Lively <milively@microsoft.com> Co-authored-by: Matt Bierner <matb@microsoft.com> Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com> Co-authored-by: Benjamin Pasero <benjamin.pasero@microsoft.com> Co-authored-by: Sandeep Somavarapu <sasomava@microsoft.com> Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Co-authored-by: Tyler James Leonhardt <me@tylerleonhardt.com> Co-authored-by: Alexandru Dima <alexdima@microsoft.com> Co-authored-by: Joao Moreno <Joao.Moreno@microsoft.com> Co-authored-by: Alan Ren <alanren@microsoft.com>
This commit is contained in:
@@ -4,13 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser';
|
||||
import { startServer } from '../server';
|
||||
|
||||
declare let self: any;
|
||||
import { startVsCodeServer } from '../server';
|
||||
|
||||
const messageReader = new BrowserMessageReader(self);
|
||||
const messageWriter = new BrowserMessageWriter(self);
|
||||
|
||||
const connection = createConnection(messageReader, messageWriter);
|
||||
|
||||
startServer(connection);
|
||||
startVsCodeServer(connection);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as l10n from '@vscode/l10n';
|
||||
|
||||
let initialized = false;
|
||||
const pendingMessages: any[] = [];
|
||||
|
||||
const messageHandler = async (e: any) => {
|
||||
if (!initialized) {
|
||||
const l10nLog: string[] = [];
|
||||
initialized = true;
|
||||
const i10lLocation = e.data.i10lLocation;
|
||||
if (i10lLocation) {
|
||||
try {
|
||||
await l10n.config({ uri: i10lLocation });
|
||||
l10nLog.push(`l10n: Configured to ${i10lLocation.toString()}.`);
|
||||
} catch (e) {
|
||||
l10nLog.push(`l10n: Problems loading ${i10lLocation.toString()} : ${e}.`);
|
||||
}
|
||||
} else {
|
||||
l10nLog.push(`l10n: No bundle configured.`);
|
||||
}
|
||||
|
||||
await import('./main');
|
||||
|
||||
if (self.onmessage !== messageHandler) {
|
||||
pendingMessages.forEach(msg => self.onmessage?.(msg));
|
||||
pendingMessages.length = 0;
|
||||
}
|
||||
|
||||
l10nLog.forEach(console.log);
|
||||
} else {
|
||||
pendingMessages.push(e);
|
||||
}
|
||||
};
|
||||
self.onmessage = messageHandler;
|
||||
@@ -3,17 +3,25 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface LsConfiguration {
|
||||
/**
|
||||
* List of file extensions should be considered as markdown.
|
||||
*
|
||||
* These should not include the leading `.`.
|
||||
*/
|
||||
readonly markdownFileExtensions: readonly string[];
|
||||
}
|
||||
import { LsConfiguration } from 'vscode-markdown-languageservice';
|
||||
|
||||
export { LsConfiguration };
|
||||
|
||||
const defaultConfig: LsConfiguration = {
|
||||
markdownFileExtensions: ['md'],
|
||||
knownLinkedToFileExtensions: [
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'webp',
|
||||
'bmp',
|
||||
'tiff',
|
||||
],
|
||||
excludePaths: [
|
||||
'**/.*',
|
||||
'**/node_modules/**',
|
||||
]
|
||||
};
|
||||
|
||||
export function getLsConfiguration(overrides: Partial<LsConfiguration>): LsConfiguration {
|
||||
|
||||
@@ -6,30 +6,45 @@
|
||||
import { Connection, Emitter } from 'vscode-languageserver';
|
||||
import { Disposable } from './util/dispose';
|
||||
|
||||
export type ValidateEnabled = 'ignore' | 'warning' | 'error';
|
||||
export type ValidateEnabled = 'ignore' | 'warning' | 'error' | 'hint';
|
||||
|
||||
interface Settings {
|
||||
export interface Settings {
|
||||
readonly markdown: {
|
||||
readonly server: {
|
||||
readonly log: 'off' | 'debug' | 'trace';
|
||||
};
|
||||
|
||||
readonly preferredMdPathExtensionStyle: 'auto' | 'includeExtension' | 'removeExtension';
|
||||
|
||||
readonly occurrencesHighlight: {
|
||||
readonly enabled: boolean;
|
||||
};
|
||||
|
||||
readonly suggest: {
|
||||
readonly paths: {
|
||||
readonly enabled: boolean;
|
||||
readonly includeWorkspaceHeaderCompletions: 'never' | 'onSingleOrDoubleHash' | 'onDoubleHash';
|
||||
};
|
||||
};
|
||||
|
||||
readonly experimental: {
|
||||
readonly validate: {
|
||||
readonly enabled: true;
|
||||
readonly referenceLinks: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
};
|
||||
readonly fragmentLinks: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
};
|
||||
readonly fileLinks: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
readonly markdownFragmentLinks: ValidateEnabled;
|
||||
};
|
||||
readonly ignoreLinks: readonly string[];
|
||||
readonly validate: {
|
||||
readonly enabled: true;
|
||||
readonly referenceLinks: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
};
|
||||
readonly fragmentLinks: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
};
|
||||
readonly fileLinks: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
readonly markdownFragmentLinks: ValidateEnabled | 'inherit';
|
||||
};
|
||||
readonly ignoredLinks: readonly string[];
|
||||
readonly unusedLinkDefinitions: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
};
|
||||
readonly duplicateLinkDefinitions: {
|
||||
readonly enabled: ValidateEnabled;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Connection, FullDocumentDiagnosticReport, UnchangedDocumentDiagnosticReport } from 'vscode-languageserver';
|
||||
import { Connection, FullDocumentDiagnosticReport, TextDocuments, UnchangedDocumentDiagnosticReport } from 'vscode-languageserver';
|
||||
import * as md from 'vscode-markdown-languageservice';
|
||||
import { disposeAll } from 'vscode-markdown-languageservice/out/util/dispose';
|
||||
import { Disposable } from 'vscode-notebook-renderer/events';
|
||||
import { URI } from 'vscode-uri';
|
||||
import { ConfigurationManager, ValidateEnabled } from '../configuration';
|
||||
import { VsCodeClientWorkspace } from '../workspace';
|
||||
import { disposeAll } from '../util/dispose';
|
||||
|
||||
const defaultDiagnosticOptions: md.DiagnosticOptions = {
|
||||
validateFileLinks: md.DiagnosticLevel.ignore,
|
||||
validateReferences: md.DiagnosticLevel.ignore,
|
||||
validateFragmentLinks: md.DiagnosticLevel.ignore,
|
||||
validateMarkdownFileLinkFragments: md.DiagnosticLevel.ignore,
|
||||
validateUnusedLinkDefinitions: md.DiagnosticLevel.ignore,
|
||||
validateDuplicateLinkDefinitions: md.DiagnosticLevel.ignore,
|
||||
ignoreLinks: [],
|
||||
};
|
||||
|
||||
@@ -24,6 +25,7 @@ function convertDiagnosticLevel(enabled: ValidateEnabled): md.DiagnosticLevel |
|
||||
case 'error': return md.DiagnosticLevel.error;
|
||||
case 'warning': return md.DiagnosticLevel.warning;
|
||||
case 'ignore': return md.DiagnosticLevel.ignore;
|
||||
case 'hint': return md.DiagnosticLevel.hint;
|
||||
default: return md.DiagnosticLevel.ignore;
|
||||
}
|
||||
}
|
||||
@@ -34,27 +36,31 @@ function getDiagnosticsOptions(config: ConfigurationManager): md.DiagnosticOptio
|
||||
return defaultDiagnosticOptions;
|
||||
}
|
||||
|
||||
const validateFragmentLinks = convertDiagnosticLevel(settings.markdown.validate.fragmentLinks.enabled);
|
||||
return {
|
||||
validateFileLinks: convertDiagnosticLevel(settings.markdown.experimental.validate.fileLinks.enabled),
|
||||
validateReferences: convertDiagnosticLevel(settings.markdown.experimental.validate.referenceLinks.enabled),
|
||||
validateFragmentLinks: convertDiagnosticLevel(settings.markdown.experimental.validate.fragmentLinks.enabled),
|
||||
validateMarkdownFileLinkFragments: convertDiagnosticLevel(settings.markdown.experimental.validate.fileLinks.markdownFragmentLinks),
|
||||
ignoreLinks: settings.markdown.experimental.validate.ignoreLinks,
|
||||
validateFileLinks: convertDiagnosticLevel(settings.markdown.validate.fileLinks.enabled),
|
||||
validateReferences: convertDiagnosticLevel(settings.markdown.validate.referenceLinks.enabled),
|
||||
validateFragmentLinks: convertDiagnosticLevel(settings.markdown.validate.fragmentLinks.enabled),
|
||||
validateMarkdownFileLinkFragments: settings.markdown.validate.fileLinks.markdownFragmentLinks === 'inherit' ? validateFragmentLinks : convertDiagnosticLevel(settings.markdown.validate.fileLinks.markdownFragmentLinks),
|
||||
validateUnusedLinkDefinitions: convertDiagnosticLevel(settings.markdown.validate.unusedLinkDefinitions.enabled),
|
||||
validateDuplicateLinkDefinitions: convertDiagnosticLevel(settings.markdown.validate.duplicateLinkDefinitions.enabled),
|
||||
ignoreLinks: settings.markdown.validate.ignoredLinks,
|
||||
};
|
||||
}
|
||||
|
||||
export function registerValidateSupport(
|
||||
connection: Connection,
|
||||
workspace: VsCodeClientWorkspace,
|
||||
workspace: md.IWorkspace,
|
||||
documents: TextDocuments<md.ITextDocument>,
|
||||
ls: md.IMdLanguageService,
|
||||
config: ConfigurationManager,
|
||||
logger: md.ILogger,
|
||||
): Disposable {
|
||||
let diagnosticOptions: md.DiagnosticOptions = defaultDiagnosticOptions;
|
||||
function updateDiagnosticsSetting(): void {
|
||||
diagnosticOptions = getDiagnosticsOptions(config);
|
||||
}
|
||||
|
||||
|
||||
const subs: Disposable[] = [];
|
||||
const manager = ls.createPullDiagnosticsManager();
|
||||
subs.push(manager);
|
||||
@@ -64,14 +70,23 @@ export function registerValidateSupport(
|
||||
connection.languages.diagnostics.refresh();
|
||||
}));
|
||||
|
||||
const emptyDiagnosticsResponse = Object.freeze({ kind: 'full', items: [] });
|
||||
|
||||
connection.languages.diagnostics.on(async (params, token): Promise<FullDocumentDiagnosticReport | UnchangedDocumentDiagnosticReport> => {
|
||||
if (!config.getSettings()?.markdown.experimental.validate.enabled) {
|
||||
return { kind: 'full', items: [] };
|
||||
logger.log(md.LogLevel.Debug, 'connection.languages.diagnostics.on', { document: params.textDocument.uri });
|
||||
|
||||
if (!config.getSettings()?.markdown.validate.enabled) {
|
||||
return emptyDiagnosticsResponse;
|
||||
}
|
||||
|
||||
const document = await workspace.openMarkdownDocument(URI.parse(params.textDocument.uri));
|
||||
const uri = URI.parse(params.textDocument.uri);
|
||||
if (!workspace.hasMarkdownDocument(uri)) {
|
||||
return emptyDiagnosticsResponse;
|
||||
}
|
||||
|
||||
const document = await workspace.openMarkdownDocument(uri);
|
||||
if (!document) {
|
||||
return { kind: 'full', items: [] };
|
||||
return emptyDiagnosticsResponse;
|
||||
}
|
||||
|
||||
const diagnostics = await manager.computeDiagnostics(document, diagnosticOptions, token);
|
||||
@@ -87,6 +102,10 @@ export function registerValidateSupport(
|
||||
connection.languages.diagnostics.refresh();
|
||||
}));
|
||||
|
||||
subs.push(documents.onDidClose(e => {
|
||||
manager.disposeDocumentResources(URI.parse(e.document.uri));
|
||||
}));
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
disposeAll(subs);
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ILogger, LogLevel } from 'vscode-markdown-languageservice';
|
||||
import * as md from 'vscode-markdown-languageservice';
|
||||
import { ConfigurationManager } from './configuration';
|
||||
import { Disposable } from './util/dispose';
|
||||
|
||||
export class LogFunctionLogger implements ILogger {
|
||||
export class LogFunctionLogger extends Disposable implements md.ILogger {
|
||||
|
||||
private static now(): string {
|
||||
const now = new Date();
|
||||
@@ -27,21 +29,53 @@ export class LogFunctionLogger implements ILogger {
|
||||
return JSON.stringify(data, undefined, 2);
|
||||
}
|
||||
|
||||
private _logLevel: md.LogLevel;
|
||||
|
||||
constructor(
|
||||
private readonly _logFn: typeof console.log
|
||||
) { }
|
||||
private readonly _logFn: typeof console.log,
|
||||
private readonly _config: ConfigurationManager,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(this._config.onDidChangeConfiguration(() => {
|
||||
this._logLevel = LogFunctionLogger.readLogLevel(this._config);
|
||||
}));
|
||||
|
||||
public log(level: LogLevel, title: string, message: string, data?: any): void {
|
||||
this.appendLine(`[${level} ${LogFunctionLogger.now()}] ${title}: ${message}`);
|
||||
this._logLevel = LogFunctionLogger.readLogLevel(this._config);
|
||||
}
|
||||
|
||||
private static readLogLevel(config: ConfigurationManager): md.LogLevel {
|
||||
switch (config.getSettings()?.markdown.server.log) {
|
||||
case 'trace': return md.LogLevel.Trace;
|
||||
case 'debug': return md.LogLevel.Debug;
|
||||
case 'off':
|
||||
default:
|
||||
return md.LogLevel.Off;
|
||||
}
|
||||
}
|
||||
|
||||
get level(): md.LogLevel { return this._logLevel; }
|
||||
|
||||
public log(level: md.LogLevel, message: string, data?: any): void {
|
||||
if (this.level < level) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.appendLine(`[${this.toLevelLabel(level)} ${LogFunctionLogger.now()}] ${message}`);
|
||||
if (data) {
|
||||
this.appendLine(LogFunctionLogger.data2String(data));
|
||||
}
|
||||
}
|
||||
|
||||
private toLevelLabel(level: md.LogLevel): string {
|
||||
switch (level) {
|
||||
case md.LogLevel.Off: return 'Off';
|
||||
case md.LogLevel.Debug: return 'Debug';
|
||||
case md.LogLevel.Trace: return 'Trace';
|
||||
}
|
||||
}
|
||||
|
||||
private appendLine(value: string): void {
|
||||
this._logFn(value);
|
||||
}
|
||||
}
|
||||
|
||||
export const consoleLogger = new LogFunctionLogger(console.log);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Connection, createConnection } from 'vscode-languageserver/node';
|
||||
import { startServer } from '../server';
|
||||
import { startVsCodeServer } from '../server';
|
||||
|
||||
// Create a connection for the server.
|
||||
const connection: Connection = createConnection();
|
||||
@@ -16,4 +16,4 @@ process.on('unhandledRejection', (e: any) => {
|
||||
connection.console.error(`Unhandled exception ${e}`);
|
||||
});
|
||||
|
||||
startServer(connection);
|
||||
startVsCodeServer(connection);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as l10n from '@vscode/l10n';
|
||||
|
||||
async function setupMain() {
|
||||
const l10nLog: string[] = [];
|
||||
|
||||
const i10lLocation = process.env['VSCODE_L10N_BUNDLE_LOCATION'];
|
||||
if (i10lLocation) {
|
||||
try {
|
||||
await l10n.config({ uri: i10lLocation });
|
||||
l10nLog.push(`l10n: Configured to ${i10lLocation.toString()}`);
|
||||
} catch (e) {
|
||||
l10nLog.push(`l10n: Problems loading ${i10lLocation.toString()} : ${e}`);
|
||||
}
|
||||
}
|
||||
await import('./main');
|
||||
|
||||
l10nLog.forEach(console.log);
|
||||
}
|
||||
setupMain();
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RequestType } from 'vscode-languageserver';
|
||||
import { FileRename, RequestType } from 'vscode-languageserver';
|
||||
import type * as lsp from 'vscode-languageserver-types';
|
||||
import type * as md from 'vscode-markdown-languageservice';
|
||||
|
||||
@@ -14,7 +14,7 @@ export const fs_readFile = new RequestType<{ uri: string }, number[], any>('mark
|
||||
export const fs_readDirectory = new RequestType<{ uri: string }, [string, { isDirectory: boolean }][], any>('markdown/fs/readDirectory');
|
||||
export const fs_stat = new RequestType<{ uri: string }, { isDirectory: boolean } | undefined, any>('markdown/fs/stat');
|
||||
|
||||
export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions }, void, any>('markdown/fs/watcher/create');
|
||||
export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions; watchParentDirs: boolean }, void, any>('markdown/fs/watcher/create');
|
||||
export const fs_watcher_delete = new RequestType<{ id: number }, void, any>('markdown/fs/watcher/delete');
|
||||
|
||||
export const findMarkdownFilesInWorkspace = new RequestType<{}, string[], any>('markdown/findMarkdownFilesInWorkspace');
|
||||
@@ -22,6 +22,9 @@ export const findMarkdownFilesInWorkspace = new RequestType<{}, string[], any>('
|
||||
|
||||
//#region To server
|
||||
export const getReferencesToFileInWorkspace = new RequestType<{ uri: string }, lsp.Location[], any>('markdown/getReferencesToFileInWorkspace');
|
||||
export const getEditForFileRenames = new RequestType<FileRename[], { participatingRenames: readonly FileRename[]; edit: lsp.WorkspaceEdit }, any>('markdown/getEditForFileRenames');
|
||||
|
||||
export const fs_watcher_onChange = new RequestType<{ id: number; uri: string; kind: 'create' | 'change' | 'delete' }, void, any>('markdown/fs/watcher/onChange');
|
||||
|
||||
export const resolveLinkTarget = new RequestType<{ linkText: string; uri: string }, md.ResolvedDocumentLinkTarget, any>('markdown/resolveLinkTarget');
|
||||
//#endregion
|
||||
|
||||
@@ -3,54 +3,92 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken, Connection, InitializeParams, InitializeResult, NotebookDocuments, TextDocuments } from 'vscode-languageserver';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { CancellationToken, CompletionRegistrationOptions, CompletionRequest, Connection, Disposable, DocumentHighlightRegistrationOptions, DocumentHighlightRequest, InitializeParams, InitializeResult, NotebookDocuments, ResponseError, TextDocuments } from 'vscode-languageserver';
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import * as lsp from 'vscode-languageserver-types';
|
||||
import * as md from 'vscode-markdown-languageservice';
|
||||
import { IDisposable } from 'vscode-markdown-languageservice/out/util/dispose';
|
||||
import { URI } from 'vscode-uri';
|
||||
import { getLsConfiguration } from './config';
|
||||
import { ConfigurationManager } from './configuration';
|
||||
import { LsConfiguration, getLsConfiguration } from './config';
|
||||
import { ConfigurationManager, Settings } from './configuration';
|
||||
import { registerValidateSupport } from './languageFeatures/diagnostics';
|
||||
import { LogFunctionLogger } from './logging';
|
||||
import * as protocol from './protocol';
|
||||
import { IDisposable } from './util/dispose';
|
||||
import { VsCodeClientWorkspace } from './workspace';
|
||||
|
||||
export async function startServer(connection: Connection) {
|
||||
interface MdServerInitializationOptions extends LsConfiguration { }
|
||||
|
||||
const organizeLinkDefKind = 'source.organizeLinkDefinitions';
|
||||
|
||||
export async function startVsCodeServer(connection: Connection) {
|
||||
const configurationManager = new ConfigurationManager(connection);
|
||||
const logger = new LogFunctionLogger(connection.console.log.bind(connection.console), configurationManager);
|
||||
|
||||
const parser = new class implements md.IMdParser {
|
||||
slugifier = md.githubSlugifier;
|
||||
|
||||
tokenize(document: md.ITextDocument): Promise<md.Token[]> {
|
||||
return connection.sendRequest(protocol.parse, { uri: document.uri.toString() });
|
||||
}
|
||||
};
|
||||
|
||||
const documents = new TextDocuments(TextDocument);
|
||||
const notebooks = new NotebookDocuments(documents);
|
||||
|
||||
const configurationManager = new ConfigurationManager(connection);
|
||||
const workspaceFactory: WorkspaceFactory = ({ connection, config, workspaceFolders }) => {
|
||||
const workspace = new VsCodeClientWorkspace(connection, config, documents, notebooks, logger);
|
||||
workspace.workspaceFolders = (workspaceFolders ?? []).map(x => URI.parse(x.uri));
|
||||
return workspace;
|
||||
};
|
||||
|
||||
let provider: md.IMdLanguageService | undefined;
|
||||
let workspace: VsCodeClientWorkspace | undefined;
|
||||
return startServer(connection, { documents, notebooks, configurationManager, logger, parser, workspaceFactory });
|
||||
}
|
||||
|
||||
type WorkspaceFactory = (config: {
|
||||
connection: Connection;
|
||||
config: LsConfiguration;
|
||||
workspaceFolders?: lsp.WorkspaceFolder[] | null;
|
||||
}) => md.IWorkspace;
|
||||
|
||||
export async function startServer(connection: Connection, serverConfig: {
|
||||
documents: TextDocuments<md.ITextDocument>;
|
||||
notebooks?: NotebookDocuments<md.ITextDocument>;
|
||||
configurationManager: ConfigurationManager;
|
||||
logger: md.ILogger;
|
||||
parser: md.IMdParser;
|
||||
workspaceFactory: WorkspaceFactory;
|
||||
}) {
|
||||
const { documents, notebooks } = serverConfig;
|
||||
|
||||
let mdLs: md.IMdLanguageService | undefined;
|
||||
|
||||
connection.onInitialize((params: InitializeParams): InitializeResult => {
|
||||
const parser = new class implements md.IMdParser {
|
||||
slugifier = md.githubSlugifier;
|
||||
const initOptions = params.initializationOptions as MdServerInitializationOptions | undefined;
|
||||
|
||||
async tokenize(document: md.ITextDocument): Promise<md.Token[]> {
|
||||
return await connection.sendRequest(protocol.parse, { uri: document.uri.toString() });
|
||||
}
|
||||
};
|
||||
const mdConfig = getLsConfiguration(initOptions ?? {});
|
||||
|
||||
const config = getLsConfiguration({
|
||||
markdownFileExtensions: params.initializationOptions.markdownFileExtensions,
|
||||
});
|
||||
|
||||
const logger = new LogFunctionLogger(connection.console.log.bind(connection.console));
|
||||
workspace = new VsCodeClientWorkspace(connection, config, documents, notebooks, logger);
|
||||
provider = md.createLanguageService({
|
||||
const workspace = serverConfig.workspaceFactory({ connection, config: mdConfig, workspaceFolders: params.workspaceFolders });
|
||||
mdLs = md.createLanguageService({
|
||||
workspace,
|
||||
parser,
|
||||
logger,
|
||||
markdownFileExtensions: config.markdownFileExtensions,
|
||||
parser: serverConfig.parser,
|
||||
logger: serverConfig.logger,
|
||||
...mdConfig,
|
||||
get preferredMdPathExtensionStyle() {
|
||||
switch (serverConfig.configurationManager.getSettings()?.markdown.preferredMdPathExtensionStyle) {
|
||||
case 'includeExtension': return md.PreferredMdPathExtensionStyle.includeExtension;
|
||||
case 'removeExtension': return md.PreferredMdPathExtensionStyle.removeExtension;
|
||||
case 'auto':
|
||||
default:
|
||||
return md.PreferredMdPathExtensionStyle.auto;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerCompletionsSupport(connection, documents, provider, configurationManager);
|
||||
registerValidateSupport(connection, workspace, provider, configurationManager);
|
||||
registerCompletionsSupport(connection, documents, mdLs, serverConfig.configurationManager);
|
||||
registerDocumentHighlightSupport(connection, documents, mdLs, serverConfig.configurationManager);
|
||||
registerValidateSupport(connection, workspace, documents, mdLs, serverConfig.configurationManager, serverConfig.logger);
|
||||
|
||||
workspace.workspaceFolders = (params.workspaceFolders ?? []).map(x => URI.parse(x.uri));
|
||||
return {
|
||||
capabilities: {
|
||||
diagnosticProvider: {
|
||||
@@ -59,11 +97,19 @@ export async function startServer(connection: Connection) {
|
||||
interFileDependencies: true,
|
||||
workspaceDiagnostics: false,
|
||||
},
|
||||
completionProvider: { triggerCharacters: ['.', '/', '#'] },
|
||||
codeActionProvider: {
|
||||
resolveProvider: true,
|
||||
codeActionKinds: [
|
||||
organizeLinkDefKind,
|
||||
'quickfix',
|
||||
'refactor',
|
||||
]
|
||||
},
|
||||
definitionProvider: true,
|
||||
documentLinkProvider: { resolveProvider: true },
|
||||
documentSymbolProvider: true,
|
||||
foldingRangeProvider: true,
|
||||
referencesProvider: true,
|
||||
renameProvider: { prepareProvider: true, },
|
||||
selectionRangeProvider: true,
|
||||
workspaceSymbolProvider: true,
|
||||
@@ -77,176 +123,241 @@ export async function startServer(connection: Connection) {
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
connection.onDocumentLinks(async (params, token): Promise<lsp.DocumentLink[]> => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await provider!.getDocumentLinks(document, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
return mdLs!.getDocumentLinks(document, token);
|
||||
});
|
||||
|
||||
connection.onDocumentLinkResolve(async (link, token): Promise<lsp.DocumentLink | undefined> => {
|
||||
try {
|
||||
return await provider!.resolveDocumentLink(link, token);
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
}
|
||||
return undefined;
|
||||
return mdLs!.resolveDocumentLink(link, token);
|
||||
});
|
||||
|
||||
connection.onDocumentSymbol(async (params, token): Promise<lsp.DocumentSymbol[]> => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await provider!.getDocumentSymbols(document, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
return mdLs!.getDocumentSymbols(document, { includeLinkDefinitions: true }, token);
|
||||
});
|
||||
|
||||
connection.onFoldingRanges(async (params, token): Promise<lsp.FoldingRange[]> => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await provider!.getFoldingRanges(document, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
return mdLs!.getFoldingRanges(document, token);
|
||||
});
|
||||
|
||||
connection.onSelectionRanges(async (params, token): Promise<lsp.SelectionRange[] | undefined> => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await provider!.getSelectionRanges(document, params.positions, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
return mdLs!.getSelectionRanges(document, params.positions, token);
|
||||
});
|
||||
|
||||
connection.onWorkspaceSymbol(async (params, token): Promise<lsp.WorkspaceSymbol[]> => {
|
||||
try {
|
||||
return await provider!.getWorkspaceSymbols(params.query, token);
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
}
|
||||
return [];
|
||||
return mdLs!.getWorkspaceSymbols(params.query, token);
|
||||
});
|
||||
|
||||
connection.onReferences(async (params, token): Promise<lsp.Location[]> => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await provider!.getReferences(document, params.position, params.context, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
return mdLs!.getReferences(document, params.position, params.context, token);
|
||||
});
|
||||
|
||||
connection.onDefinition(async (params, token): Promise<lsp.Definition | undefined> => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await provider!.getDefinition(document, params.position, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
return mdLs!.getDefinition(document, params.position, token);
|
||||
});
|
||||
|
||||
connection.onPrepareRename(async (params, token) => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await provider!.prepareRename(document, params.position, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
return await mdLs!.prepareRename(document, params.position, token);
|
||||
} catch (e) {
|
||||
if (e instanceof md.RenameNotSupportedAtLocationError) {
|
||||
throw new ResponseError(0, e.message);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
connection.onRenameRequest(async (params, token) => {
|
||||
try {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
const edit = await provider!.getRenameEdit(document, params.position, params.newName, token);
|
||||
console.log(JSON.stringify(edit));
|
||||
return edit;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
return mdLs!.getRenameEdit(document, params.position, params.newName, token);
|
||||
});
|
||||
|
||||
interface OrganizeLinkActionData {
|
||||
readonly uri: string;
|
||||
}
|
||||
|
||||
connection.onCodeAction(async (params, token) => {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (params.context.only?.some(kind => kind === 'source' || kind.startsWith('source.'))) {
|
||||
const action: lsp.CodeAction = {
|
||||
title: l10n.t("Organize link definitions"),
|
||||
kind: organizeLinkDefKind,
|
||||
data: <OrganizeLinkActionData>{ uri: document.uri }
|
||||
};
|
||||
return [action];
|
||||
}
|
||||
|
||||
return mdLs!.getCodeActions(document, params.range, params.context, token);
|
||||
});
|
||||
|
||||
connection.onCodeActionResolve(async (codeAction, token) => {
|
||||
if (codeAction.kind === organizeLinkDefKind) {
|
||||
const data = codeAction.data as OrganizeLinkActionData;
|
||||
const document = documents.get(data.uri);
|
||||
if (!document) {
|
||||
return codeAction;
|
||||
}
|
||||
|
||||
const edits = (await mdLs?.organizeLinkDefinitions(document, { removeUnused: true }, token)) || [];
|
||||
codeAction.edit = {
|
||||
changes: {
|
||||
[data.uri]: edits
|
||||
}
|
||||
};
|
||||
return codeAction;
|
||||
}
|
||||
|
||||
return codeAction;
|
||||
});
|
||||
|
||||
connection.onRequest(protocol.getReferencesToFileInWorkspace, (async (params: { uri: string }, token: CancellationToken) => {
|
||||
try {
|
||||
return await provider!.getFileReferences(URI.parse(params.uri), token);
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
return mdLs!.getFileReferences(URI.parse(params.uri), token);
|
||||
}));
|
||||
|
||||
connection.onRequest(protocol.getEditForFileRenames, (async (params, token: CancellationToken) => {
|
||||
const result = await mdLs!.getRenameFilesInWorkspaceEdit(params.map(x => ({ oldUri: URI.parse(x.oldUri), newUri: URI.parse(x.newUri) })), token);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
return undefined;
|
||||
|
||||
return {
|
||||
edit: result.edit,
|
||||
participatingRenames: result.participatingRenames.map(rename => ({ oldUri: rename.oldUri.toString(), newUri: rename.newUri.toString() }))
|
||||
};
|
||||
}));
|
||||
|
||||
connection.onRequest(protocol.resolveLinkTarget, (async (params, token: CancellationToken) => {
|
||||
return mdLs!.resolveLinkTarget(params.linkText, URI.parse(params.uri), token);
|
||||
}));
|
||||
|
||||
documents.listen(connection);
|
||||
notebooks.listen(connection);
|
||||
notebooks?.listen(connection);
|
||||
connection.listen();
|
||||
}
|
||||
|
||||
|
||||
function registerCompletionsSupport(
|
||||
connection: Connection,
|
||||
documents: TextDocuments<TextDocument>,
|
||||
ls: md.IMdLanguageService,
|
||||
function registerDynamicClientFeature(
|
||||
config: ConfigurationManager,
|
||||
): IDisposable {
|
||||
// let registration: Promise<IDisposable> | undefined;
|
||||
isEnabled: (settings: Settings | undefined) => boolean,
|
||||
register: () => Promise<Disposable>,
|
||||
) {
|
||||
let registration: Promise<IDisposable> | undefined;
|
||||
function update() {
|
||||
// TODO: client still makes the request in this case. Figure our how to properly unregister.
|
||||
return;
|
||||
// const settings = config.getSettings();
|
||||
// if (settings?.markdown.suggest.paths.enabled) {
|
||||
// if (!registration) {
|
||||
// registration = connection.client.register(CompletionRequest.type);
|
||||
// }
|
||||
// } else {
|
||||
// registration?.then(x => x.dispose());
|
||||
// registration = undefined;
|
||||
// }
|
||||
}
|
||||
|
||||
connection.onCompletion(async (params, token): Promise<lsp.CompletionItem[]> => {
|
||||
try {
|
||||
const settings = config.getSettings();
|
||||
if (!settings?.markdown.suggest.paths.enabled) {
|
||||
return [];
|
||||
const settings = config.getSettings();
|
||||
if (isEnabled(settings)) {
|
||||
if (!registration) {
|
||||
registration = register();
|
||||
}
|
||||
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return await ls.getCompletionItems(document, params.position, params.context!, token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
} else {
|
||||
registration?.then(x => x.dispose());
|
||||
registration = undefined;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
update();
|
||||
return config.onDidChangeConfiguration(() => update());
|
||||
}
|
||||
|
||||
function registerCompletionsSupport(
|
||||
connection: Connection,
|
||||
documents: TextDocuments<md.ITextDocument>,
|
||||
ls: md.IMdLanguageService,
|
||||
config: ConfigurationManager,
|
||||
): IDisposable {
|
||||
function getIncludeWorkspaceHeaderCompletions(): md.IncludeWorkspaceHeaderCompletions {
|
||||
switch (config.getSettings()?.markdown.suggest.paths.includeWorkspaceHeaderCompletions) {
|
||||
case 'onSingleOrDoubleHash': return md.IncludeWorkspaceHeaderCompletions.onSingleOrDoubleHash;
|
||||
case 'onDoubleHash': return md.IncludeWorkspaceHeaderCompletions.onDoubleHash;
|
||||
case 'never':
|
||||
default: return md.IncludeWorkspaceHeaderCompletions.never;
|
||||
}
|
||||
}
|
||||
|
||||
connection.onCompletion(async (params, token): Promise<lsp.CompletionItem[]> => {
|
||||
const settings = config.getSettings();
|
||||
if (!settings?.markdown.suggest.paths.enabled) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
// TODO: remove any type after picking up new release with correct types
|
||||
return ls.getCompletionItems(document, params.position, {
|
||||
...(params.context || {}),
|
||||
includeWorkspaceHeaderCompletions: getIncludeWorkspaceHeaderCompletions(),
|
||||
} as any, token);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
return registerDynamicClientFeature(config, (settings) => !!settings?.markdown.suggest.paths.enabled, () => {
|
||||
const registrationOptions: CompletionRegistrationOptions = {
|
||||
documentSelector: null,
|
||||
triggerCharacters: ['.', '/', '#'],
|
||||
};
|
||||
return connection.client.register(CompletionRequest.type, registrationOptions);
|
||||
});
|
||||
}
|
||||
|
||||
function registerDocumentHighlightSupport(
|
||||
connection: Connection,
|
||||
documents: TextDocuments<md.ITextDocument>,
|
||||
mdLs: md.IMdLanguageService,
|
||||
configurationManager: ConfigurationManager
|
||||
) {
|
||||
connection.onDocumentHighlight(async (params, token) => {
|
||||
const settings = configurationManager.getSettings();
|
||||
if (!settings?.markdown.occurrencesHighlight.enabled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return mdLs!.getDocumentHighlights(document, params.position, token);
|
||||
});
|
||||
|
||||
return registerDynamicClientFeature(configurationManager, (settings) => !!settings?.markdown.occurrencesHighlight.enabled, () => {
|
||||
const registrationOptions: DocumentHighlightRegistrationOptions = {
|
||||
documentSelector: null,
|
||||
};
|
||||
return connection.client.register(DocumentHighlightRequest.type, registrationOptions);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @returns New array with all falsy values removed. The original array IS NOT modified.
|
||||
*/
|
||||
export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
|
||||
return <T[]>array.filter(e => !!e);
|
||||
}
|
||||
@@ -3,14 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export class MultiDisposeError extends Error {
|
||||
constructor(
|
||||
public readonly errors: any[]
|
||||
) {
|
||||
super(`Encountered errors while disposing of store. Errors: [${errors.join(', ')}]`);
|
||||
}
|
||||
}
|
||||
|
||||
export function disposeAll(disposables: Iterable<IDisposable>) {
|
||||
const errors: any[] = [];
|
||||
|
||||
@@ -25,7 +17,7 @@ export function disposeAll(disposables: Iterable<IDisposable>) {
|
||||
if (errors.length === 1) {
|
||||
throw errors[0];
|
||||
} else if (errors.length > 1) {
|
||||
throw new MultiDisposeError(errors);
|
||||
throw new AggregateError(errors, 'Encountered errors while disposing of store');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,21 +52,3 @@ export abstract class Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
export class DisposableStore extends Disposable {
|
||||
private readonly items = new Set<IDisposable>();
|
||||
|
||||
public override dispose() {
|
||||
super.dispose();
|
||||
disposeAll(this.items);
|
||||
this.items.clear();
|
||||
}
|
||||
|
||||
public add<T extends IDisposable>(item: T): T {
|
||||
if (this.isDisposed) {
|
||||
console.warn('Adding to disposed store. Item will be leaked');
|
||||
}
|
||||
|
||||
this.items.add(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { URI, Utils } from 'vscode-uri';
|
||||
import { LsConfiguration } from '../config';
|
||||
|
||||
export function looksLikeMarkdownPath(config: LsConfiguration, resolvedHrefPath: URI) {
|
||||
return config.markdownFileExtensions.includes(Utils.extname(URI.from(resolvedHrefPath)).toLowerCase().replace('.', ''));
|
||||
return config.markdownFileExtensions.includes(Utils.extname(resolvedHrefPath).toLowerCase().replace('.', ''));
|
||||
}
|
||||
|
||||
export function isMarkdownFile(document: TextDocument) {
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Connection, Emitter, FileChangeType, NotebookDocuments, TextDocuments } from 'vscode-languageserver';
|
||||
import { Connection, Emitter, FileChangeType, NotebookDocuments, Position, Range, TextDocuments } from 'vscode-languageserver';
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import * as md from 'vscode-markdown-languageservice';
|
||||
import { ContainingDocumentContext, FileWatcherOptions, IFileSystemWatcher } from 'vscode-markdown-languageservice/out/workspace';
|
||||
import { URI } from 'vscode-uri';
|
||||
import { LsConfiguration } from './config';
|
||||
import * as protocol from './protocol';
|
||||
import { coalesce } from './util/arrays';
|
||||
import { isMarkdownFile, looksLikeMarkdownPath } from './util/file';
|
||||
import { Limiter } from './util/limiter';
|
||||
import { ResourceMap } from './util/resourceMap';
|
||||
@@ -18,6 +16,70 @@ import { Schemes } from './util/schemes';
|
||||
|
||||
declare const TextDecoder: any;
|
||||
|
||||
class VsCodeDocument implements md.ITextDocument {
|
||||
|
||||
private inMemoryDoc?: TextDocument;
|
||||
private onDiskDoc?: TextDocument;
|
||||
|
||||
readonly uri: string;
|
||||
|
||||
constructor(uri: string, init: { inMemoryDoc: TextDocument });
|
||||
constructor(uri: string, init: { onDiskDoc: TextDocument });
|
||||
constructor(uri: string, init: { inMemoryDoc?: TextDocument; onDiskDoc?: TextDocument }) {
|
||||
this.uri = uri;
|
||||
this.inMemoryDoc = init?.inMemoryDoc;
|
||||
this.onDiskDoc = init?.onDiskDoc;
|
||||
}
|
||||
|
||||
get version(): number {
|
||||
return this.inMemoryDoc?.version ?? this.onDiskDoc?.version ?? 0;
|
||||
}
|
||||
|
||||
get lineCount(): number {
|
||||
return this.inMemoryDoc?.lineCount ?? this.onDiskDoc?.lineCount ?? 0;
|
||||
}
|
||||
|
||||
getText(range?: Range): string {
|
||||
if (this.inMemoryDoc) {
|
||||
return this.inMemoryDoc.getText(range);
|
||||
}
|
||||
|
||||
if (this.onDiskDoc) {
|
||||
return this.onDiskDoc.getText(range);
|
||||
}
|
||||
|
||||
throw new Error('Document has been closed');
|
||||
}
|
||||
|
||||
positionAt(offset: number): Position {
|
||||
if (this.inMemoryDoc) {
|
||||
return this.inMemoryDoc.positionAt(offset);
|
||||
}
|
||||
|
||||
if (this.onDiskDoc) {
|
||||
return this.onDiskDoc.positionAt(offset);
|
||||
}
|
||||
|
||||
throw new Error('Document has been closed');
|
||||
}
|
||||
|
||||
hasInMemoryDoc(): boolean {
|
||||
return !!this.inMemoryDoc;
|
||||
}
|
||||
|
||||
isDetached(): boolean {
|
||||
return !this.onDiskDoc && !this.inMemoryDoc;
|
||||
}
|
||||
|
||||
setInMemoryDoc(doc: TextDocument | undefined) {
|
||||
this.inMemoryDoc = doc;
|
||||
}
|
||||
|
||||
setOnDiskDoc(doc: TextDocument | undefined) {
|
||||
this.onDiskDoc = doc;
|
||||
}
|
||||
}
|
||||
|
||||
export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
|
||||
private readonly _onDidCreateMarkdownDocument = new Emitter<md.ITextDocument>();
|
||||
@@ -29,14 +91,14 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
private readonly _onDidDeleteMarkdownDocument = new Emitter<URI>();
|
||||
public readonly onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocument.event;
|
||||
|
||||
private readonly _documentCache = new ResourceMap<md.ITextDocument>();
|
||||
private readonly _documentCache = new ResourceMap<VsCodeDocument>();
|
||||
|
||||
private readonly _utf8Decoder = new TextDecoder('utf-8');
|
||||
|
||||
private _watcherPool = 0;
|
||||
private readonly _watchers = new Map<number, {
|
||||
readonly resource: URI;
|
||||
readonly options: FileWatcherOptions;
|
||||
readonly options: md.FileWatcherOptions;
|
||||
readonly onDidChange: Emitter<URI>;
|
||||
readonly onDidCreate: Emitter<URI>;
|
||||
readonly onDidDelete: Emitter<URI>;
|
||||
@@ -50,45 +112,117 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
private readonly logger: md.ILogger,
|
||||
) {
|
||||
documents.onDidOpen(e => {
|
||||
this._documentCache.delete(URI.parse(e.document.uri));
|
||||
if (this.isRelevantMarkdownDocument(e.document)) {
|
||||
this._onDidCreateMarkdownDocument.fire(e.document);
|
||||
if (!this.isRelevantMarkdownDocument(e.document)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.TextDocument.onDidOpen', { document: e.document.uri });
|
||||
|
||||
const uri = URI.parse(e.document.uri);
|
||||
const doc = this._documentCache.get(uri);
|
||||
|
||||
if (doc) {
|
||||
// File already existed on disk
|
||||
doc.setInMemoryDoc(e.document);
|
||||
|
||||
// The content visible to the language service may have changed since the in-memory doc
|
||||
// may differ from the one on-disk. To be safe we always fire a change event.
|
||||
this._onDidChangeMarkdownDocument.fire(doc);
|
||||
} else {
|
||||
// We're creating the file for the first time
|
||||
const doc = new VsCodeDocument(e.document.uri, { inMemoryDoc: e.document });
|
||||
this._documentCache.set(uri, doc);
|
||||
this._onDidCreateMarkdownDocument.fire(doc);
|
||||
}
|
||||
});
|
||||
|
||||
documents.onDidChangeContent(e => {
|
||||
if (this.isRelevantMarkdownDocument(e.document)) {
|
||||
this._onDidChangeMarkdownDocument.fire(e.document);
|
||||
if (!this.isRelevantMarkdownDocument(e.document)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.TextDocument.onDidChanceContent', { document: e.document.uri });
|
||||
|
||||
const uri = URI.parse(e.document.uri);
|
||||
const entry = this._documentCache.get(uri);
|
||||
if (entry) {
|
||||
entry.setInMemoryDoc(e.document);
|
||||
this._onDidChangeMarkdownDocument.fire(entry);
|
||||
}
|
||||
});
|
||||
|
||||
documents.onDidClose(e => {
|
||||
this._documentCache.delete(URI.parse(e.document.uri));
|
||||
documents.onDidClose(async e => {
|
||||
if (!this.isRelevantMarkdownDocument(e.document)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.TextDocument.onDidClose', { document: e.document.uri });
|
||||
|
||||
const uri = URI.parse(e.document.uri);
|
||||
const doc = this._documentCache.get(uri);
|
||||
if (!doc) {
|
||||
// Document was never opened
|
||||
return;
|
||||
}
|
||||
|
||||
doc.setInMemoryDoc(undefined);
|
||||
if (doc.isDetached()) {
|
||||
// The document has been fully closed
|
||||
this.doDeleteDocument(uri);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that if file has been deleted on disk.
|
||||
// This can happen when directories are renamed / moved. VS Code's file system watcher does not
|
||||
// notify us when this happens.
|
||||
if (!(await this.statBypassingCache(uri))) {
|
||||
if (this._documentCache.get(uri) === doc && !doc.hasInMemoryDoc()) {
|
||||
this.doDeleteDocument(uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The document still exists on disk
|
||||
// To be safe, tell the service that the document has changed because the
|
||||
// in-memory doc contents may be different than the disk doc contents.
|
||||
this._onDidChangeMarkdownDocument.fire(doc);
|
||||
});
|
||||
|
||||
connection.onDidChangeWatchedFiles(async ({ changes }) => {
|
||||
for (const change of changes) {
|
||||
const resource = URI.parse(change.uri);
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: onDidChangeWatchedFiles', `${change.type}: ${resource}`);
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.onDidChangeWatchedFiles', { type: change.type, resource: resource.toString() });
|
||||
switch (change.type) {
|
||||
case FileChangeType.Changed: {
|
||||
this._documentCache.delete(resource);
|
||||
const document = await this.openMarkdownDocument(resource);
|
||||
if (document) {
|
||||
this._onDidChangeMarkdownDocument.fire(document);
|
||||
const entry = this._documentCache.get(resource);
|
||||
if (entry) {
|
||||
// Refresh the on-disk state
|
||||
const document = await this.openMarkdownDocumentFromFs(resource);
|
||||
if (document) {
|
||||
this._onDidChangeMarkdownDocument.fire(document);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FileChangeType.Created: {
|
||||
const document = await this.openMarkdownDocument(resource);
|
||||
if (document) {
|
||||
this._onDidCreateMarkdownDocument.fire(document);
|
||||
const entry = this._documentCache.get(resource);
|
||||
if (entry) {
|
||||
// Create or update the on-disk state
|
||||
const document = await this.openMarkdownDocumentFromFs(resource);
|
||||
if (document) {
|
||||
this._onDidCreateMarkdownDocument.fire(document);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FileChangeType.Deleted: {
|
||||
this._documentCache.delete(resource);
|
||||
this._onDidDeleteMarkdownDocument.fire(resource);
|
||||
const entry = this._documentCache.get(resource);
|
||||
if (entry) {
|
||||
entry.setOnDiskDoc(undefined);
|
||||
if (entry.isDetached()) {
|
||||
this.doDeleteDocument(resource);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -96,7 +230,7 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
});
|
||||
|
||||
connection.onRequest(protocol.fs_watcher_onChange, params => {
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: fs_watcher_onChange', `${params.kind}: ${params.uri}`);
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.fs_watcher_onChange', { kind: params.kind, uri: params.uri });
|
||||
|
||||
const watcher = this._watchers.get(params.id);
|
||||
if (!watcher) {
|
||||
@@ -128,29 +262,35 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
}
|
||||
|
||||
async getAllMarkdownDocuments(): Promise<Iterable<md.ITextDocument>> {
|
||||
// Add opened files (such as untitled files)
|
||||
const openTextDocumentResults = this.documents.all()
|
||||
.filter(doc => this.isRelevantMarkdownDocument(doc));
|
||||
|
||||
const allDocs = new ResourceMap<md.ITextDocument>();
|
||||
for (const doc of openTextDocumentResults) {
|
||||
allDocs.set(URI.parse(doc.uri), doc);
|
||||
}
|
||||
|
||||
// And then add files on disk
|
||||
const maxConcurrent = 20;
|
||||
|
||||
const foundFiles = new ResourceMap<void>();
|
||||
const limiter = new Limiter<md.ITextDocument | undefined>(maxConcurrent);
|
||||
|
||||
// Add files on disk
|
||||
const resources = await this.connection.sendRequest(protocol.findMarkdownFilesInWorkspace, {});
|
||||
const onDiskResults = await Promise.all(resources.map(strResource => {
|
||||
await Promise.all(resources.map(strResource => {
|
||||
return limiter.queue(async () => {
|
||||
const resource = URI.parse(strResource);
|
||||
if (allDocs.has(resource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = await this.openMarkdownDocument(resource);
|
||||
if (doc) {
|
||||
foundFiles.set(resource);
|
||||
allDocs.set(resource, doc);
|
||||
}
|
||||
return doc;
|
||||
});
|
||||
}));
|
||||
|
||||
// Add opened files (such as untitled files)
|
||||
const openTextDocumentResults = await Promise.all(this.documents.all()
|
||||
.filter(doc => !foundFiles.has(URI.parse(doc.uri)) && this.isRelevantMarkdownDocument(doc)));
|
||||
|
||||
return coalesce([...onDiskResults, ...openTextDocumentResults]);
|
||||
return allDocs.values();
|
||||
}
|
||||
|
||||
hasMarkdownDocument(resource: URI): boolean {
|
||||
@@ -165,10 +305,21 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
|
||||
const matchingDocument = this.documents.get(resource.toString());
|
||||
if (matchingDocument) {
|
||||
this._documentCache.set(resource, matchingDocument);
|
||||
return matchingDocument;
|
||||
let entry = this._documentCache.get(resource);
|
||||
if (entry) {
|
||||
entry.setInMemoryDoc(matchingDocument);
|
||||
} else {
|
||||
entry = new VsCodeDocument(resource.toString(), { inMemoryDoc: matchingDocument });
|
||||
this._documentCache.set(resource, entry);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
return this.openMarkdownDocumentFromFs(resource);
|
||||
}
|
||||
|
||||
private async openMarkdownDocumentFromFs(resource: URI): Promise<md.ITextDocument | undefined> {
|
||||
if (!looksLikeMarkdownPath(this.config, resource)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -180,7 +331,9 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
|
||||
// We assume that markdown is in UTF-8
|
||||
const text = this._utf8Decoder.decode(bytes);
|
||||
const doc = TextDocument.create(resource.toString(), 'markdown', 0, text);
|
||||
const doc = new VsCodeDocument(resource.toString(), {
|
||||
onDiskDoc: TextDocument.create(resource.toString(), 'markdown', 0, text)
|
||||
});
|
||||
this._documentCache.set(resource, doc);
|
||||
return doc;
|
||||
} catch (e) {
|
||||
@@ -189,19 +342,28 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
}
|
||||
|
||||
async stat(resource: URI): Promise<md.FileStat | undefined> {
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: stat', `${resource}`);
|
||||
if (this._documentCache.has(resource) || this.documents.get(resource.toString())) {
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.stat', { resource: resource.toString() });
|
||||
if (this._documentCache.has(resource)) {
|
||||
return { isDirectory: false };
|
||||
}
|
||||
return this.connection.sendRequest(protocol.fs_stat, { uri: resource.toString() });
|
||||
return this.statBypassingCache(resource);
|
||||
}
|
||||
|
||||
private async statBypassingCache(resource: URI): Promise<md.FileStat | undefined> {
|
||||
const uri = resource.toString();
|
||||
if (this.documents.get(uri)) {
|
||||
return { isDirectory: false };
|
||||
}
|
||||
const fsResult = await this.connection.sendRequest(protocol.fs_stat, { uri });
|
||||
return fsResult ?? undefined; // Force convert null to undefined
|
||||
}
|
||||
|
||||
async readDirectory(resource: URI): Promise<[string, md.FileStat][]> {
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: readDir', `${resource}`);
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.readDir', { resource: resource.toString() });
|
||||
return this.connection.sendRequest(protocol.fs_readDirectory, { uri: resource.toString() });
|
||||
}
|
||||
|
||||
getContainingDocument(resource: URI): ContainingDocumentContext | undefined {
|
||||
getContainingDocument(resource: URI): md.ContainingDocumentContext | undefined {
|
||||
if (resource.scheme === Schemes.notebookCell) {
|
||||
const nb = this.notebooks.findNotebookDocumentForCell(resource.toString());
|
||||
if (nb) {
|
||||
@@ -214,9 +376,9 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
watchFile(resource: URI, options: FileWatcherOptions): IFileSystemWatcher {
|
||||
watchFile(resource: URI, options: md.FileWatcherOptions): md.IFileSystemWatcher {
|
||||
const id = this._watcherPool++;
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: watchFile', `(${id}) ${resource}`);
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.watchFile', { id, resource: resource.toString() });
|
||||
|
||||
const entry = {
|
||||
resource,
|
||||
@@ -231,6 +393,7 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
id,
|
||||
uri: resource.toString(),
|
||||
options,
|
||||
watchParentDirs: true,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -238,7 +401,7 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
onDidChange: entry.onDidChange.event,
|
||||
onDidDelete: entry.onDidDelete.event,
|
||||
dispose: () => {
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: disposeWatcher', `(${id}) ${resource}`);
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.disposeWatcher', { id, resource: resource.toString() });
|
||||
this.connection.sendRequest(protocol.fs_watcher_delete, { id });
|
||||
this._watchers.delete(id);
|
||||
}
|
||||
@@ -248,4 +411,11 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
||||
private isRelevantMarkdownDocument(doc: TextDocument) {
|
||||
return isMarkdownFile(doc) && URI.parse(doc.uri).scheme !== 'vscode-bulkeditpreview';
|
||||
}
|
||||
|
||||
private doDeleteDocument(uri: URI) {
|
||||
this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace.deleteDocument', { document: uri.toString() });
|
||||
|
||||
this._documentCache.delete(uri);
|
||||
this._onDidDeleteMarkdownDocument.fire(uri);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user