Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/many-pens-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-language-server": patch
---

[perf/refactor]: utils improvements in language-server
50 changes: 32 additions & 18 deletions packages/language-server/src/lib/documents/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export function extractTemplateTag(source: string, html?: HTMLDocument): TagInfo
/**
* Get the line and character based on the offset
* @param offset The index of the position
* @param text The text for which the position should be retrived
* @param text The text for which the position should be retrieved
* @param lineOffsets number Array with offsets for each line. Computed if not given
*/
export function positionAt(
Expand Down Expand Up @@ -222,7 +222,7 @@ export function positionAt(
/**
* Get the offset of the line and character position
* @param position Line and character position
* @param text The text for which the offset should be retrived
* @param text The text for which the offset should be retrieved
* @param lineOffsets number Array with offsets for each line. Computed if not given
*/
export function offsetAt(
Expand Down Expand Up @@ -281,14 +281,21 @@ export function isRangeInTag(
}

export function getTextInRange(range: Range, text: string) {
return text.substring(offsetAt(range.start, text), offsetAt(range.end, text));
const lineOffsets = getLineOffsets(text);
const start = offsetAt(range.start, text, lineOffsets);
const end = offsetAt(range.end, text, lineOffsets);
return text.substring(start, end);
}

export function getLineAtPosition(position: Position, text: string) {
return text.substring(
offsetAt({ line: position.line, character: 0 }, text),
offsetAt({ line: position.line, character: Number.MAX_VALUE }, text)
const lineOffsets = getLineOffsets(text);
const lineStart = offsetAt({ line: position.line, character: 0 }, text, lineOffsets);
const lineEnd = offsetAt(
{ line: position.line, character: Number.MAX_VALUE },
text,
lineOffsets
);
return text.substring(lineStart, lineEnd);
}

/**
Expand Down Expand Up @@ -446,22 +453,29 @@ export function getLangAttribute(...tags: Array<TagInformation | null>): string
* `{#if {a: true}.a}`
*/
export function isInsideMoustacheTag(html: string, tagStart: number | null, position: number) {
const searchEnd = Math.max(position - 1, 0);

if (tagStart === null) {
// Not inside <tag ... >
const charactersBeforePosition = html.substring(0, position);
return (
Math.max(
// TODO make this just check for '{'?
// Theoretically, someone could do {a < b} in a simple moustache tag
charactersBeforePosition.lastIndexOf('{#'),
charactersBeforePosition.lastIndexOf('{:'),
charactersBeforePosition.lastIndexOf('{@')
) > charactersBeforePosition.lastIndexOf('}')
);
// TODO make this just check for '{'?
// Theoretically, someone could do {a < b} in a simple moustache tag
const lastHash = html.lastIndexOf('{#', searchEnd);
const lastColon = html.lastIndexOf('{:', searchEnd);
const lastAt = html.lastIndexOf('{@', searchEnd);
const lastClose = html.lastIndexOf('}', searchEnd);

const lastOpen = Math.max(lastHash, lastColon, lastAt);
return lastOpen > lastClose;
} else {
// Inside <tag ... >
const charactersInNode = html.substring(tagStart, position);
return charactersInNode.lastIndexOf('{') > charactersInNode.lastIndexOf('}');
const lastOpen = html.lastIndexOf('{', searchEnd);
const lastClose = html.lastIndexOf('}', searchEnd);

// Ensure we only consider braces inside the tag region
const effectiveLastOpen = lastOpen >= tagStart ? lastOpen : -1;
const effectiveLastClose = lastClose >= tagStart ? lastClose : -1;

return effectiveLastOpen > effectiveLastClose;
}
}

Expand Down
25 changes: 12 additions & 13 deletions packages/language-server/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { isEqual, sum, uniqWith } from 'lodash';
import { FoldingRange, Node } from 'vscode-html-languageservice';
import { isEqual, uniqWith } from 'lodash';
import { Node } from 'vscode-html-languageservice';
import { Position, Range } from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { Document, TagInformation } from './lib/documents';

type Predicate<T> = (x: T) => boolean;

Expand Down Expand Up @@ -137,13 +136,13 @@ export function isNotNullOrUndefined<T>(val: T | undefined | null): val is T {
* a second function determines it should.
*
* @param fn The function with it's argument
* @param determineIfSame The function which determines if the previous invocation should be canceld or not
* @param miliseconds Number of miliseconds to debounce
* @param determineIfSame The function which determines if the previous invocation should be canceled or not
* @param milliseconds Number of milliseconds to debounce
*/
export function debounceSameArg<T>(
fn: (arg: T) => void,
shouldCancelPrevious: (newArg: T, prevArg?: T) => boolean,
miliseconds: number
milliseconds: number
): (arg: T) => void {
let timeout: any;
let prevArg: T | undefined;
Expand All @@ -157,34 +156,34 @@ export function debounceSameArg<T>(
timeout = setTimeout(() => {
fn(arg);
prevArg = undefined;
}, miliseconds);
}, milliseconds);
};
}

/**
* Debounces a function but also waits at minimum the specified number of miliseconds until
* Debounces a function but also waits at minimum the specified number of milliseconds until
* the next invocation. This avoids needless calls when a synchronous call (like diagnostics)
* took too long and the whole timeout of the next call was eaten up already.
*
* @param fn The function
* @param miliseconds Number of miliseconds to debounce/throttle
* @param milliseconds Number of milliseconds to debounce/throttle
*/
export function debounceThrottle(fn: () => void, miliseconds: number): () => void {
export function debounceThrottle(fn: () => void, milliseconds: number): () => void {
let timeout: any;
let lastInvocation = Date.now() - miliseconds;
let lastInvocation = Date.now() - milliseconds;

function maybeCall() {
clearTimeout(timeout);

timeout = setTimeout(() => {
if (Date.now() - lastInvocation < miliseconds) {
if (Date.now() - lastInvocation < milliseconds) {
maybeCall();
return;
}

fn();
lastInvocation = Date.now();
}, miliseconds);
}, milliseconds);
}

return maybeCall;
Expand Down
Loading