Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement language status items #756

Merged
merged 8 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/changes/756.breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The `esbonio.server.enabledInPyFiles` configuration option has been removed, use `esbonio.server.documentSelector` instead
1 change: 1 addition & 0 deletions code/changes/756.enhancement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added the `esbonio.server.documentSelector` option, granting the user fine grained control over which files the server is enabled in.
25 changes: 21 additions & 4 deletions code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,28 @@
"default": true,
"description": "Enable/Disable the language server"
},
"esbonio.server.enabledInPyFiles": {
"esbonio.server.documentSelector": {
"scope": "window",
"type": "boolean",
"default": true,
"description": "Enable/Disable the language server in Python files."
"type": "array",
"items": {
"type": "object",
"properties": {
"scheme": {
"type": "string",
"description": "The URI scheme that this selector applies to"
},
"language": {
"type": "string",
"description": "The language id this selector will select"
},
"pattern": {
"type": "string",
"description": "Only select uris that match the given pattern"
}
}
},
"default": [],
"description": "Override the extension's default document selector"
},
"esbonio.server.startupModule": {
"scope": "window",
Expand Down
9 changes: 9 additions & 0 deletions code/src/common/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export namespace Server {
export const REQUIRED_PYTHON = "3.8.0"

export const DEFAULT_SELECTOR = [
{ scheme: 'file', language: 'restructuredtext' },
{ scheme: 'file', language: 'markdown' },
// { scheme: 'file', language: 'python' }
]
}

export namespace Commands {
Expand Down Expand Up @@ -27,5 +33,8 @@ export namespace Notifications {
export const SCROLL_EDITOR = "editor/scroll"
export const VIEW_SCROLL = "view/scroll"

export const SPHINX_CLIENT_CREATED = "sphinx/clientCreated"
export const SPHINX_CLIENT_ERRORED = "sphinx/clientErrored"
export const SPHINX_CLIENT_DESTROYED = "sphinx/clientDestroyed"
export const SPHINX_APP_CREATED = "sphinx/appCreated"
}
98 changes: 86 additions & 12 deletions code/src/node/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,84 @@ import {
LanguageClientOptions,
ResponseError,
ServerOptions,
State
State,
TextDocumentFilter
} from "vscode-languageclient/node";

import { OutputChannelLogger } from "../common/log";
import { PythonManager } from "./python";
import { Commands, Events, Notifications } from '../common/constants';
import { Commands, Events, Notifications, Server } from '../common/constants';


export interface SphinxInfo {
export interface SphinxClientConfig {

/**
* The python command used to launch the client
*/
pythonCommand: string[]

/**
* The sphinx-build command in use
*/
buildCommand: string[]

/**
* The working directory of the client
*/
cwd: string

}

export interface ClientCreatedNotification {
/**
* A unique id for this client
*/
id: string

/**
* A unique id used to refer to this Sphinx application instance.
* The configuration scope at which the client was created
*/
scope: string

/**
* The final configuration
*/
config: SphinxClientConfig

}

/**
* The payload of a ``sphinx/clientErrored`` notification
*/
export interface ClientErroredNotification {

/**
* A unique id for the client
*/
id: string

/**
* Short description of the error.
*/
error: string

/**
* Detailed description of the error.
*/
detail: string
}


export interface ClientDestroyedNotification {
/**
* A unique id for this client
*/
id: string
}

export interface SphinxInfo {


/**
* Sphinx's version number
*/
Expand Down Expand Up @@ -52,6 +115,18 @@ export interface SphinxInfo {
src_dir: string
}

export interface AppCreatedNotification {

/**
* A unique id for this client
*/
id: string

/**
* Details about the created application.
*/
application: SphinxInfo
}

export class EsbonioClient {

Expand Down Expand Up @@ -243,6 +318,9 @@ export class EsbonioClient {
let methods = [
Notifications.SCROLL_EDITOR,
Notifications.SPHINX_APP_CREATED,
Notifications.SPHINX_CLIENT_CREATED,
Notifications.SPHINX_CLIENT_ERRORED,
Notifications.SPHINX_CLIENT_DESTROYED,
]

for (let method of methods) {
Expand All @@ -258,15 +336,11 @@ export class EsbonioClient {
*/
private getLanguageClientOptions(config: vscode.WorkspaceConfiguration): LanguageClientOptions {

let documentSelector = [
{ scheme: 'file', language: 'restructuredtext' },
{ scheme: 'file', language: 'markdown' },
]

if (config.get<boolean>('server.enabledInPyFiles')) {
documentSelector.push(
{ scheme: 'file', language: 'python' }
)

let documentSelector = config.get<TextDocumentFilter[]>("server.documentSelector")
if (!documentSelector || documentSelector.length === 0) {
documentSelector = Server.DEFAULT_SELECTOR
}

let clientOptions: LanguageClientOptions = {
Expand Down
148 changes: 103 additions & 45 deletions code/src/node/status.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import * as vscode from 'vscode';
import * as path from 'path';
import * as vscode from 'vscode';

import { TextDocumentFilter } from 'vscode-languageclient';
import { Events, Notifications, Server } from '../common/constants';
import { OutputChannelLogger } from "../common/log";
import { EsbonioClient, SphinxInfo } from './client';
import { Events, Notifications } from '../common/constants';
import {
AppCreatedNotification,
ClientCreatedNotification,
ClientDestroyedNotification,
ClientErroredNotification,
EsbonioClient
} from './client';

interface StatusItemFields {
busy?: boolean
Expand Down Expand Up @@ -31,35 +38,107 @@ export class StatusManager {

client.addHandler(
Notifications.SPHINX_APP_CREATED,
(params: SphinxInfo) => { this.createApp(params) }
(params: AppCreatedNotification) => { this.appCreated(params) }
)

client.addHandler(
Notifications.SPHINX_CLIENT_CREATED,
(params: ClientCreatedNotification) => { this.clientCreated(params) }
)

client.addHandler(
Notifications.SPHINX_CLIENT_ERRORED,
(params: ClientErroredNotification) => { this.clientErrored(params) }
)

client.addHandler(
Notifications.SPHINX_CLIENT_DESTROYED,
(params: ClientDestroyedNotification) => { this.clientDestroyed(params) }
)
}

private createApp(info: SphinxInfo) {
private clientCreated(params: ClientCreatedNotification) {
this.logger.debug(`${Notifications.SPHINX_CLIENT_CREATED}: ${JSON.stringify(params, undefined, 2)}`)
let sphinxConfig = params.config

let confUri = vscode.Uri.file(info.conf_dir)
let workspaceFolder = vscode.workspace.getWorkspaceFolder(confUri)
if (!workspaceFolder) {
this.logger.error(`Unable to find workspace containing: ${info.conf_dir}`)
return
let config = vscode.workspace.getConfiguration("esbonio.server")
let documentSelector = config.get<TextDocumentFilter[]>("documentSelector")
if (!documentSelector || documentSelector.length === 0) {
documentSelector = Server.DEFAULT_SELECTOR
}

let selector: vscode.DocumentFilter[] = []
let defaultPattern = path.join(sphinxConfig.cwd, "**", "*")
for (let docSelector of documentSelector) {
selector.push({
scheme: docSelector.scheme,
language: docSelector.language,
pattern: docSelector.pattern || defaultPattern
})
}

let confPattern = uriToPattern(confUri)
selector.push({ language: "python", pattern: confPattern })
this.setStatusItem(
params.id,
"sphinx",
"Sphinx[starting]",
{
selector: selector,
busy: true,
detail: sphinxConfig.buildCommand.join(" "),
severity: vscode.LanguageStatusSeverity.Information
}
)
this.setStatusItem(
params.id,
"python",
"Python",
{
selector: selector,
detail: sphinxConfig.pythonCommand.join(" "),
command: { title: "Change Interpreter", command: "python.setInterpreter" },
severity: vscode.LanguageStatusSeverity.Information
}
)
}

let srcUri = vscode.Uri.file(info.src_dir)
let srcPattern = uriToPattern(srcUri);
selector.push({ language: 'restructuredtext', pattern: srcPattern })
private clientErrored(params: ClientErroredNotification) {
this.logger.debug(`${Notifications.SPHINX_CLIENT_ERRORED}: ${JSON.stringify(params, undefined, 2)}`)

this.setStatusItem(
params.id,
"sphinx",
"Sphinx[failed]",
{
busy: false,
detail: params.error,
severity: vscode.LanguageStatusSeverity.Error
}
)
}

let itemId = `${workspaceFolder.uri}`
let buildUri = vscode.Uri.file(info.build_dir)
this.setStatusItem(itemId, "sphinx", `Sphinx v${info.version}`, { selector: selector })
this.setStatusItem(itemId, "builder", `Builder - ${info.builder_name}`, { selector: selector })
this.setStatusItem(itemId, "srcdir", `Source - ${renderPath(workspaceFolder, srcUri)}`, { selector: selector })
this.setStatusItem(itemId, "confdir", `Config - ${renderPath(workspaceFolder, confUri)}`, { selector: selector })
this.setStatusItem(itemId, "builddir", `Build - ${renderPath(workspaceFolder, buildUri)}`, { selector: selector })
private clientDestroyed(params: ClientDestroyedNotification) {
this.logger.debug(`${Notifications.SPHINX_CLIENT_DESTROYED}: ${JSON.stringify(params, undefined, 2)}`)

for (let [key, item] of this.statusItems.entries()) {
if (key.startsWith(params.id)) {
item.dispose()
this.statusItems.delete(key)
}
}
}

private appCreated(params: AppCreatedNotification) {
this.logger.debug(`${Notifications.SPHINX_APP_CREATED}: ${JSON.stringify(params, undefined, 2)}`)
let sphinx = params.application

this.setStatusItem(
params.id,
"sphinx",
`Sphinx[${sphinx.builder_name}] v${sphinx.version}`,
{
busy: false,
}
)
}

private serverStop(_params: any) {
Expand All @@ -70,12 +149,12 @@ export class StatusManager {
}

private setStatusItem(
sphinxId: string,
id: string,
name: string,
value: string,
params?: StatusItemFields,
) {
let key = `${sphinxId}-${name.toLocaleLowerCase().replace(' ', '-')}`
let key = `${id}-${name.toLocaleLowerCase().replace(' ', '-')}`
let statusItem = this.statusItems.get(key)

if (!statusItem) {
Expand Down Expand Up @@ -108,24 +187,3 @@ export class StatusManager {
}
}
}

function renderPath(workspace: vscode.WorkspaceFolder, uri: vscode.Uri): string {
let workspacePath = workspace.uri.fsPath
let uriPath = uri.fsPath

let result = uriPath

if (uriPath.startsWith(workspacePath)) {
result = path.join('.', result.replace(workspacePath, ''))
}

if (result.length > 50) {
result = '...' + result.slice(result.length - 47)
}

return result
}

function uriToPattern(uri: vscode.Uri) {
return path.join(uri.fsPath, "**", "*").replace(/\\/g, '/');
}
Loading
Loading