Skip to content

Commit

Permalink
Add configuration option requireConfig (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
remcohaszing committed Sep 1, 2023
1 parent dd63b00 commit c3023e7
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
coverage/
node_modules/
/test/.testremarkrc.json
*.log
*.d.ts
*.tgz
92 changes: 88 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,18 @@
* This can be used to ship a processor with your package, to be used if no
* processor is found locally.
* If this isn’t passed, a warning is shown if `processorName` can’t be found.
* @property {string} configurationSection
* This option will be used to give the client a hint of which configuration
* section to use.
* For example VSCode extensions use this to pick only settings that use this
* as a prefix in order to prevent conflicts and reduce the amount of data
* sent to the language server.
*
* @typedef {EngineFields & LanguageServerFields} Options
*
* @typedef UnifiedLanguageServerSettings
* @property {boolean} [requireConfig=false]
* If true, files will only be checked if a configuration file is present.
*/

import path from 'node:path'
Expand All @@ -42,6 +52,7 @@ import {
CodeActionKind,
Diagnostic,
DiagnosticSeverity,
DidChangeConfigurationNotification,
Position,
ProposedFeatures,
Range,
Expand Down Expand Up @@ -122,6 +133,7 @@ function lspDocumentToVfile(document, cwd) {
* Configuration for `unified-engine` and the language server.
*/
export function createUnifiedLanguageServer({
configurationSection,
ignoreName,
packageField,
pluginPrefix,
Expand All @@ -135,14 +147,49 @@ export function createUnifiedLanguageServer({
const documents = new TextDocuments(TextDocument)
/** @type {Set<string>} */
const workspaces = new Set()
/** @type {UnifiedLanguageServerSettings} */
const globalSettings = {requireConfig: false}
/** @type {Map<string, Promise<UnifiedLanguageServerSettings>>} */
const documentSettings = new Map()
let hasWorkspaceFolderCapability = false
let hasConfigurationCapability = false

/**
* @param {string} scopeUri
* @returns {Promise<UnifiedLanguageServerSettings>}
*/
async function getDocumentSettings(scopeUri) {
if (!hasConfigurationCapability) {
return globalSettings
}

let result = documentSettings.get(scopeUri)
if (!result) {
result = connection.workspace
.getConfiguration({scopeUri, section: configurationSection})
.then(
/** @param {Record<string, unknown>} raw */
(raw) => ({requireConfig: Boolean(raw.requireConfig)})
)
documentSettings.set(scopeUri, result)
}

return result
}

/**
* @param {string} cwd
* @param {VFile[]} files
* @param {boolean} alwaysStringify
* @param {boolean} ignoreUnconfigured
* @returns {Promise<VFile[]>}
*/
async function processWorkspace(cwd, files, alwaysStringify) {
async function processWorkspace(
cwd,
files,
alwaysStringify,
ignoreUnconfigured
) {
/** @type {EngineOptions['processor']} */
let processor

Expand Down Expand Up @@ -190,6 +237,7 @@ export function createUnifiedLanguageServer({
cwd,
files,
ignoreName,
ignoreUnconfigured,
packageField,
pluginPrefix,
plugins,
Expand Down Expand Up @@ -233,6 +281,8 @@ export function createUnifiedLanguageServer({
.sort((a, b) => b.length - a.length)
/** @type {Map<string, Array<VFile>>} */
const workspacePathToFiles = new Map()
/** @type {Map<string, Array<VFile>>} */
const workspacePathToFilesRequireConfig = new Map()

await Promise.all(
textDocuments.map(async (textDocument) => {
Expand Down Expand Up @@ -269,18 +319,28 @@ export function createUnifiedLanguageServer({

if (!cwd) return

const configuration = await getDocumentSettings(textDocument.uri)

const file = lspDocumentToVfile(textDocument, cwd)

const files = workspacePathToFiles.get(cwd) || []
workspacePathToFiles.set(cwd, [...files, file])
const filesMap = configuration.requireConfig
? workspacePathToFilesRequireConfig
: workspacePathToFiles
const files = filesMap.get(cwd) || []
files.push(file)
filesMap.set(cwd, files)
})
)

/** @type {Array<Promise<Array<VFile>>>} */
const promises = []

for (const [cwd, files] of workspacePathToFiles) {
promises.push(processWorkspace(cwd, files, alwaysStringify))
promises.push(processWorkspace(cwd, files, alwaysStringify, false))
}

for (const [cwd, files] of workspacePathToFilesRequireConfig) {
promises.push(processWorkspace(cwd, files, alwaysStringify, true))
}

const listsOfFiles = await Promise.all(promises)
Expand Down Expand Up @@ -324,6 +384,9 @@ export function createUnifiedLanguageServer({
workspaces.add(event.rootUri)
}

hasConfigurationCapability = Boolean(
event.capabilities.workspace && event.capabilities.workspace.configuration
)
hasWorkspaceFolderCapability = Boolean(
event.capabilities.workspace &&
event.capabilities.workspace.workspaceFolders
Expand All @@ -345,6 +408,10 @@ export function createUnifiedLanguageServer({
})

connection.onInitialized(() => {
if (hasConfigurationCapability) {
connection.client.register(DidChangeConfigurationNotification.type)
}

if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders((event) => {
for (const workspace of event.removed) {
Expand Down Expand Up @@ -399,13 +466,30 @@ export function createUnifiedLanguageServer({
version,
diagnostics: []
})
documentSettings.delete(uri)
})

// Check everything again if the file system watched by the client changes.
connection.onDidChangeWatchedFiles(() => {
checkDocuments(...documents.all())
})

connection.onDidChangeConfiguration((change) => {
if (hasConfigurationCapability) {
// Reset all cached document settings
documentSettings.clear()
} else {
globalSettings.requireConfig = Boolean(
/** @type {Omit<typeof change, 'settings'> & { settings: Record<string, unknown> }} */ (
change
).settings.requireConfig
)
}

// Revalidate all open text documents
checkDocuments(...documents.all())
})

connection.onCodeAction((event) => {
/** @type {CodeAction[]} */
const codeActions = []
Expand Down
6 changes: 6 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Create a **[language server][]** based on **[unified][]** ecosystems.
* [Examples](#examples)
* [Types](#types)
* [Language Server features](#language-server-features)
* [Configuration](#configuration)
* [Compatibility](#compatibility)
* [Related](#related)
* [Contribute](#contribute)
Expand Down Expand Up @@ -205,6 +206,11 @@ server features:
Any messages collected are published to the client using
`textDocument/publishDiagnostics`.

### Configuration

* `requireConfig` (default: `false`)
— If true, files will only be checked if a configuration file is present.

## Compatibility

Projects maintained by the unified collective are compatible with all maintained
Expand Down
1 change: 1 addition & 0 deletions test/code-actions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {createUnifiedLanguageServer} from 'unified-language-server'

createUnifiedLanguageServer({
configurationSection: 'remark',
processorName: 'remark',
processorSpecifier: 'remark',
plugins: [warn]
Expand Down
1 change: 1 addition & 0 deletions test/folder/remark-with-cwd.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {createUnifiedLanguageServer} from 'unified-language-server'

createUnifiedLanguageServer({
configurationSection: 'remark',
processorName: 'remark',
processorSpecifier: 'remark',
plugins: [warn]
Expand Down
Loading

0 comments on commit c3023e7

Please sign in to comment.