Skip to content

Commit 2b607ec

Browse files
authored
Merge pull request #185 from mkslanc/manual-session-control
Manual session control
2 parents 5c03eb9 + 1f07bcf commit 2b607ec

File tree

11 files changed

+185
-69
lines changed

11 files changed

+185
-69
lines changed

README.md

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ let worker = new Worker(new URL('./webworker.js', import.meta.url));
1616

1717
// Create an Ace editor
1818
let editor = ace.edit("container", {
19-
mode: new TypescriptMode() // Set the mode of the editor to Typescript
19+
mode: new TypescriptMode() // Set the mode of the editor to Typescript
2020
});
2121

2222
// Create a language provider for web worker (
@@ -28,6 +28,41 @@ languageProvider.registerEditor(editor);
2828
2929
[Example webworker.js with all services](https://github.com/mkslanc/ace-linters/blob/main/packages/demo/webworker-lsp/webworker.ts)
3030
31+
## New features in 1.8.1
32+
- add `manualSessionControl` provider option to disable automatic session registration. When enabled, you must manually handle session changes:
33+
```javascript
34+
// Create provider with manual session control
35+
let languageProvider = LanguageProvider.create(worker, {
36+
manualSessionControl: true
37+
});
38+
39+
// Register sessions manually
40+
languageProvider.registerSession(editor.session, editor, {
41+
filePath: 'path/to/file.ts',
42+
joinWorkspaceURI: true
43+
});
44+
45+
// Handle session changes manually
46+
editor.on("changeSession", ({session}) => {
47+
languageProvider.registerSession(session, editor, session.lspConfig);
48+
});
49+
```
50+
- add `setSessionLspConfig` method to set LSP configuration on Ace sessions:
51+
```javascript
52+
// Set LSP configuration on session
53+
languageProvider.setSessionLspConfig(editor.session, {
54+
filePath: 'src/components/MyComponent.tsx',
55+
joinWorkspaceURI: true
56+
});
57+
```
58+
- add `setDocumentOptions` method to replace deprecated `setSessionOptions`:
59+
```javascript
60+
// Configure document-specific options (replaces setSessionOptions)
61+
languageProvider.setDocumentOptions(editor.session, {
62+
// service-specific options here
63+
});
64+
```
65+
3166
## New Features in 1.2.0
3267
- add `setProviderOptions` method to `LanguageProvider` to set options for client.
3368
- add experimental semantic tokens support (turned off by default). To turn on semantic tokens, set `semanticTokens` to
@@ -142,15 +177,15 @@ import {AceLanguageClient} from "ace-linters/build/ace-language-client";
142177
// Create a web worker
143178
let worker = new Worker(new URL('./webworker.js', import.meta.url));
144179
const serverData = {
145-
module: () => import("ace-linters/build/language-client"),
146-
modes: "json",
147-
type: "webworker",
148-
worker: worker,
180+
module: () => import("ace-linters/build/language-client"),
181+
modes: "json",
182+
type: "webworker",
183+
worker: worker,
149184
}
150185

151186
// Create an Ace editor
152187
let editor = ace.edit("container", {
153-
mode: new TypescriptMode() // Set the mode of the editor to Typescript
188+
mode: new TypescriptMode() // Set the mode of the editor to Typescript
154189
});
155190

156191
// Create a language provider for web worker
@@ -166,35 +201,13 @@ languageProvider.registerEditor(editor);
166201
**[!]** You need to describe server similar to that example:
167202
[Example server](https://github.com/mkslanc/ace-linters/blob/main/packages/demo/webworker-json-rpc/webworker.ts)
168203
169-
## Supported LSP Capabilities
170-
171-
- **Text Document Synchronization** (incremental changes)
172-
- **Diagnostics**
173-
- Related document support
174-
- Diagnostic tags (Unnecessary, Deprecated)
175-
- Related diagnostic information support
176-
- **Hover**
177-
- Markdown and plaintext content formats
178-
- **Formatting**
179-
- **Completions**
180-
- Snippet support
181-
- Documentation formats (Markdown, plaintext)
182-
- **Signature Help**
183-
- Documentation formats (Markdown, plaintext)
184-
- Active parameter highlighting support
185-
- **Document Highlight**
186-
- **Semantic Tokens**
187-
- Relative format tokens
188-
- Range requests supported
189-
- Augments syntax tokens
190-
- **Code Actions**
191-
- **Inline Completions**
192-
- **Workspace Capabilities**
193-
- Configuration change notifications
194-
- Command execution
195-
- Workspace edits (`applyEdit` supported)
196-
- **Window Capabilities**
197-
- `showDocument` request support
204+
## Supported LSP capabilities:
205+
- Text Document Synchronization (with incremental changes)
206+
- Hover
207+
- Diagnostics
208+
- Formatting
209+
- Completions
210+
- Signature Help
198211
199212
[Full list of capabilities](https://github.com/mkslanc/ace-linters/wiki/Client-LSP-capabilities)
200213
@@ -209,15 +222,10 @@ Ace linters supports the following languages by default with webworkers approach
209222
- YAML *powered by* [Yaml Language Server](https://github.com/redhat-developer/yaml-language-server)
210223
- XML *powered by* [XML-Tools](https://github.com/SAP/xml-tools)
211224
- Javascript, JSX *powered by* [Eslint](https://github.com/eslint/eslint)
225+
- Python *powered by* [Ruff](https://github.com/charliermarsh/ruff)
212226
213227
## Supported languages via extensions
214228
- MySQL, FlinkSQL, SparkSQL, HiveSQL, TrinoSQL, PostgreSQL, Impala SQL, PL/SQL *with* [ace-sql-linter](https://www.npmjs.com/package/ace-sql-linter)
215-
- Clang *with* [ace-clang-linter](https://www.npmjs.com/package/ace-clang-linter)
216-
- Dart *with* [ace-dart-linter](https://www.npmjs.com/package/ace-dart-linter)
217-
- Go *with* [ace-go-linter](https://www.npmjs.com/package/ace-go-linter)
218-
- Lua *with* [ace-lua-linter](https://www.npmjs.com/package/ace-lua-linter)
219-
- Python *with* [ace-python-linter](https://www.npmjs.com/package/ace-python-linter)
220-
- Zig *with* [ace-zig-linter](https://www.npmjs.com/package/ace-zig-linter)
221229
222230
## Installation
223231

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ace-linters-root",
3-
"version": "1.8.0",
3+
"version": "1.8.1",
44
"scripts": {
55
"build:parts": "npm run build -ws",
66
"build": "npm run build:parts && webpack",

packages/ace-linters/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,41 @@ languageProvider.registerEditor(editor);
2828
2929
[Example webworker.js with all services](https://github.com/mkslanc/ace-linters/blob/main/packages/demo/webworker-lsp/webworker.ts)
3030
31+
## New features in 1.8.1
32+
- add `manualSessionControl` provider option to disable automatic session registration. When enabled, you must manually handle session changes:
33+
```javascript
34+
// Create provider with manual session control
35+
let languageProvider = LanguageProvider.create(worker, {
36+
manualSessionControl: true
37+
});
38+
39+
// Register sessions manually
40+
languageProvider.registerSession(editor.session, editor, {
41+
filePath: 'path/to/file.ts',
42+
joinWorkspaceURI: true
43+
});
44+
45+
// Handle session changes manually
46+
editor.on("changeSession", ({session}) => {
47+
languageProvider.registerSession(session, editor, session.lspConfig);
48+
});
49+
```
50+
- add `setSessionLspConfig` method to set LSP configuration on Ace sessions:
51+
```javascript
52+
// Set LSP configuration on session
53+
languageProvider.setSessionLspConfig(editor.session, {
54+
filePath: 'src/components/MyComponent.tsx',
55+
joinWorkspaceURI: true
56+
});
57+
```
58+
- add `setDocumentOptions` method to replace deprecated `setSessionOptions`:
59+
```javascript
60+
// Configure document-specific options (replaces setSessionOptions)
61+
languageProvider.setDocumentOptions(editor.session, {
62+
// service-specific options here
63+
});
64+
```
65+
3166
## New Features in 1.2.0
3267
- add `setProviderOptions` method to `LanguageProvider` to set options for client.
3368
- add experimental semantic tokens support (turned off by default). To turn on semantic tokens, set `semanticTokens` to

packages/ace-linters/src/language-provider.ts

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
ProviderOptions,
2424
ServiceFeatures,
2525
ServiceOptions,
26-
ServiceOptionsMap, ServiceStruct, SessionInitialConfig,
26+
ServiceOptionsMap, ServiceStruct, SessionLspConfig,
2727
SupportedServices,
2828
Tooltip
2929
} from "./types/language-service";
@@ -169,11 +169,20 @@ export class LanguageProvider {
169169
* @param session The Ace edit session to update with the file path.
170170
* @param config config to set
171171
*/
172-
setSessionFilePath(session: Ace.EditSession, config: SessionInitialConfig) {
172+
setSessionFilePath(session: Ace.EditSession, config: SessionLspConfig) {
173173
this.$getSessionLanguageProvider(session)?.setFilePath(config.filePath, config.joinWorkspaceURI);
174174
}
175175

176-
private $registerSession = (session: Ace.EditSession, editor: Ace.Editor, config?: SessionInitialConfig) => {
176+
/**
177+
* Registers a new editing session with the editor and associates it with a language provider.
178+
* If a language provider for the specified editing session does not already exist, it initializes
179+
* and stores a new session-specific language provider.
180+
*
181+
* @param session - The Ace EditSession object to be registered, representing a specific editing session.
182+
* @param editor - The Ace Editor instance associated with the editing session.
183+
* @param [config] - An optional configuration object for initializing the session.
184+
*/
185+
registerSession = (session: Ace.EditSession, editor: Ace.Editor, config?: SessionLspConfig) => {
177186
if (!this.$sessionLanguageProviders[session["id"]]) {
178187
this.$sessionLanguageProviders[session["id"]] = new SessionLanguageProvider(this, session, editor, this.$messageController, config);
179188
}
@@ -182,6 +191,18 @@ export class LanguageProvider {
182191
}
183192
}
184193

194+
/**
195+
* Sets the Language Server Protocol (LSP) configuration for the given session.
196+
*
197+
* @param session - The editor session to which the LSP configuration will be applied.
198+
* @param config - The LSP configuration to set for the session.
199+
* @return The updated editor session with the applied LSP configuration.
200+
*/
201+
setSessionLspConfig(session: Ace.EditSession, config: SessionLspConfig) {
202+
session.lspConfig = config;
203+
return session;
204+
}
205+
185206
private $getSessionLanguageProvider(session: Ace.EditSession): SessionLanguageProvider {
186207
return this.$sessionLanguageProviders[session["id"]];
187208
}
@@ -197,12 +218,14 @@ export class LanguageProvider {
197218
* @param editor - The Ace editor instance to be registered.
198219
* @param [config] - Configuration options for the session.
199220
*/
200-
registerEditor(editor: Ace.Editor, config?: SessionInitialConfig) {
221+
registerEditor(editor: Ace.Editor, config?: SessionLspConfig) {
201222
if (!this.editors.includes(editor))
202223
this.$registerEditor(editor);
203-
this.$registerSession(editor.session, editor, config);
224+
config = config ?? editor.session.lspConfig;
225+
this.registerSession(editor.session, editor, config);
204226
}
205227

228+
206229
codeActionCallback: (codeActions: CodeActionsByService[]) => void;
207230

208231
/**
@@ -278,7 +301,11 @@ export class LanguageProvider {
278301
AceEditor.getConstructor(editor);
279302

280303
editor.setOption("useWorker", false);
281-
editor.on("changeSession", ({session}) => this.$registerSession(session, editor));
304+
305+
306+
if (!this.options.manualSessionControl) {
307+
editor.on("changeSession", ({session}) => this.registerSession(session, editor, session.lspConfig));
308+
}
282309

283310
if (this.options.functionality!.completion || this.options.functionality!.inlineCompletion) {
284311
this.$registerCompleters(editor);
@@ -405,11 +432,19 @@ export class LanguageProvider {
405432
}
406433

407434
/**
408-
* Sets global options for the specified service.
435+
* Configures global options that apply to all documents handled by the specified language service.
436+
*
437+
* Global options serve as default settings for all documents processed by a service when no
438+
* document-specific options are provided. These options affect language service behavior across
439+
* the entire workspace, including validation rules, formatting preferences, completion settings,
440+
* and service-specific configurations.
409441
*
410-
* @param serviceName - The name of the service for which to set global options.
411-
* @param options - The options to set for the specified service.
412-
* @param {boolean} [merge=false] - Indicates whether to merge the provided options with the existing options. Defaults to false.
442+
* @param serviceName - The identifier of the language service to configure. Must be a valid
443+
* service name from the supported services (e.g., 'typescript', 'json', 'html').
444+
* @param options - The global configuration options specific to the language service. The structure
445+
* varies by service type.
446+
* @param {boolean} [merge=false] - Indicates whether to merge the provided options with the existing options.
447+
* Defaults to false.
413448
*/
414449
setGlobalOptions<T extends keyof ServiceOptionsMap>(serviceName: T & string, options: ServiceOptionsMap[T], merge = false) {
415450
this.$messageController.setGlobalOptions(serviceName, options, merge);
@@ -437,12 +472,25 @@ export class LanguageProvider {
437472
*
438473
* @param session - The Ace editor session to configure.
439474
* @param options - The configuration options to be applied to the session.
475+
* @deprecated Use `setDocumentOptions` instead. This method will be removed in the future.
440476
*/
441477
setSessionOptions<OptionsType extends ServiceOptions>(session: Ace.EditSession, options: OptionsType) {
442478
let sessionLanguageProvider = this.$getSessionLanguageProvider(session);
443479
sessionLanguageProvider.setOptions(options);
444480
}
445481

482+
/**
483+
* Sets configuration options for a document associated with the specified editor session.
484+
*
485+
* @param session - The Ace editor session representing the document to configure.
486+
* @param options - The service options to apply. The exact shape depends on the language services
487+
* active for this session (e.g. JSON schema settings).
488+
*/
489+
setDocumentOptions<OptionsType extends ServiceOptions>(session: Ace.EditSession, options: OptionsType) {
490+
let sessionLanguageProvider = this.$getSessionLanguageProvider(session);
491+
sessionLanguageProvider.setOptions(options);
492+
}
493+
446494
/**
447495
* Configures the specified features for a given service.
448496
*

packages/ace-linters/src/services/language-client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ export class LanguageClient extends BaseService implements LanguageService {
205205
}
206206

207207
addDocument(document: lsp.TextDocumentItem) {//TODO: this need to be async to avoid race condition
208+
if (this.getDocument(document.uri)) {
209+
console.warn(document.uri + ' already exists');
210+
return;
211+
}
208212
super.addDocument(document);
209213
const textDocumentMessage: lsp.DidOpenTextDocumentParams = {
210214
textDocument: document

packages/ace-linters/src/session-language-provider.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Ace} from "ace-code";
22
import {ComboDocumentIdentifier, IMessageController} from "./types/message-controller-interface";
3-
import {AceRangeData, ServiceOptions, SessionInitialConfig} from "./types/language-service";
3+
import {AceRangeData, ServiceOptions, SessionLspConfig} from "./types/language-service";
44
import {MarkerGroup} from "./ace/marker_group";
55
import type {LanguageProvider} from "./language-provider";
66
import * as lsp from "vscode-languageserver-protocol";
@@ -53,7 +53,7 @@ export class SessionLanguageProvider {
5353
* @param messageController - The `IMessageController` instance for handling messages.
5454
* @param config
5555
*/
56-
constructor(provider: LanguageProvider, session: Ace.EditSession, editor: Ace.Editor, messageController: IMessageController, config?: SessionInitialConfig) {
56+
constructor(provider: LanguageProvider, session: Ace.EditSession, editor: Ace.Editor, messageController: IMessageController, config?: SessionLspConfig) {
5757
this.$provider = provider;
5858
this.$messageController = messageController;
5959
this.session = session;
@@ -90,19 +90,24 @@ export class SessionLanguageProvider {
9090
* Increments the document version and updates the internal document URI and identifier.
9191
*
9292
* @param {string} filePath - The new file path for the document.
93-
* @param {boolean} [joinWorkspaceURI] - Optional flag to indicate whether to join the file path with the workspace URI.
93+
* @param {boolean} [joinWorkspaceURI] - when true the given path is treated as relative and will be joined with
94+
* the workspace’s root URI to form the final canonical URI. When false (or omitted) filePath is just transformed to
95+
* URI.
9496
*/
9597
setFilePath(filePath: string, joinWorkspaceURI?: boolean) {
9698
this.enqueueIfNotConnected(() => {
9799
this.session.doc.version++;
98100
this.$filePath = filePath;
99101
const previousComboId = this.comboDocumentIdentifier;
100102
this.initDocumentUri(true, joinWorkspaceURI);
103+
if (previousComboId.documentUri === this.comboDocumentIdentifier.documentUri) { //no need to rename
104+
return;
105+
}
101106
this.$messageController.renameDocument(previousComboId, this.comboDocumentIdentifier.documentUri, this.session.doc.version);
102107
})
103108
};
104109

105-
private $init(config?: SessionInitialConfig) {
110+
private $init(config?: SessionLspConfig) {
106111
if (config?.filePath) {
107112
this.$filePath = config.filePath;
108113
}

packages/ace-linters/src/types/ace-extension.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
import { DecodedSemanticTokens } from "../type-converters/lsp/semantic-tokens";
1+
import {DecodedSemanticTokens} from "../type-converters/lsp/semantic-tokens";
2+
import {SessionLspConfig} from "./language-service";
23

34
declare module "ace-code/src/edit_session" {
45

56
interface EditSession {
67
setSemanticTokens: (tokens: DecodedSemanticTokens | undefined) => void;
8+
lspConfig?: SessionLspConfig
79
}
810
}
911

1012
declare module "ace-code/src/background_tokenizer" {
1113
interface BackgroundTokenizer {
1214
semanticTokens: DecodedSemanticTokens | undefined;
15+
1316
$tokenizeRow(row: number): import("ace-code").Ace.Token[];
17+
1418
$worker: () => void;
1519
$updateOnChange: () => void;
1620
}

0 commit comments

Comments
 (0)