mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-03 05:13:35 +00:00
Merge pull request #19056 from Giga-Bowser/fix-syntax-tree-crlf
fix: Properly handle CRLF line endings in the syntax tree view
This commit is contained in:
commit
9f1ad04e72
4 changed files with 183 additions and 78 deletions
File diff suppressed because one or more lines are too long
|
|
@ -361,10 +361,7 @@ export function syntaxTreeReveal(): Cmd {
|
||||||
const activeEditor = vscode.window.activeTextEditor;
|
const activeEditor = vscode.window.activeTextEditor;
|
||||||
|
|
||||||
if (activeEditor !== undefined) {
|
if (activeEditor !== undefined) {
|
||||||
const start = activeEditor.document.positionAt(element.start);
|
const newSelection = new vscode.Selection(element.range.start, element.range.end);
|
||||||
const end = activeEditor.document.positionAt(element.end);
|
|
||||||
|
|
||||||
const newSelection = new vscode.Selection(start, end);
|
|
||||||
|
|
||||||
activeEditor.selection = newSelection;
|
activeEditor.selection = newSelection;
|
||||||
activeEditor.revealRange(newSelection);
|
activeEditor.revealRange(newSelection);
|
||||||
|
|
@ -378,15 +375,12 @@ function elementToString(
|
||||||
depth: number = 0,
|
depth: number = 0,
|
||||||
): string {
|
): string {
|
||||||
let result = " ".repeat(depth);
|
let result = " ".repeat(depth);
|
||||||
const start = element.istart ?? element.start;
|
const offsets = element.inner?.offsets ?? element.offsets;
|
||||||
const end = element.iend ?? element.end;
|
|
||||||
|
|
||||||
result += `${element.kind}@${start}..${end}`;
|
result += `${element.kind}@${offsets.start}..${offsets.end}`;
|
||||||
|
|
||||||
if (element.type === "Token") {
|
if (element.type === "Token") {
|
||||||
const startPosition = activeDocument.positionAt(element.start);
|
const text = activeDocument.getText(element.range).replaceAll("\r\n", "\n");
|
||||||
const endPosition = activeDocument.positionAt(element.end);
|
|
||||||
const text = activeDocument.getText(new vscode.Range(startPosition, endPosition));
|
|
||||||
// JSON.stringify quotes and escapes the string for us.
|
// JSON.stringify quotes and escapes the string for us.
|
||||||
result += ` ${JSON.stringify(text)}\n`;
|
result += ` ${JSON.stringify(text)}\n`;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -384,9 +384,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = e.textEditor.document.offsetAt(selection.start);
|
const result = this.syntaxTreeProvider?.getElementByRange(selection);
|
||||||
const end = e.textEditor.document.offsetAt(selection.end);
|
|
||||||
const result = this.syntaxTreeProvider?.getElementByRange(start, end);
|
|
||||||
if (result !== undefined) {
|
if (result !== undefined) {
|
||||||
await this.syntaxTreeView?.reveal(result);
|
await this.syntaxTreeView?.reveal(result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,7 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
|
||||||
if (editor !== undefined) {
|
if (editor !== undefined) {
|
||||||
const start = editor.document.positionAt(element.start);
|
const text = editor.document.getText(element.range);
|
||||||
const end = editor.document.positionAt(element.end);
|
|
||||||
const range = new vscode.Range(start, end);
|
|
||||||
|
|
||||||
const text = editor.document.getText(range);
|
|
||||||
item.tooltip = new vscode.MarkdownString().appendCodeblock(text, "rust");
|
item.tooltip = new vscode.MarkdownString().appendCodeblock(text, "rust");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,14 +70,61 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
|
||||||
if (editor && isRustEditor(editor)) {
|
if (editor && isRustEditor(editor)) {
|
||||||
const params = { textDocument: { uri: editor.document.uri.toString() }, range: null };
|
const params = { textDocument: { uri: editor.document.uri.toString() }, range: null };
|
||||||
const fileText = await this.ctx.client.sendRequest(ra.viewSyntaxTree, params);
|
const fileText = await this.ctx.client.sendRequest(ra.viewSyntaxTree, params);
|
||||||
this.root = JSON.parse(fileText, (_key, value: SyntaxElement) => {
|
this.root = JSON.parse(fileText, (_key, value: RawElement): SyntaxElement => {
|
||||||
if (value.type === "Node") {
|
if (value.type !== "Node" && value.type !== "Token") {
|
||||||
for (const child of value.children) {
|
// This is something other than a RawElement.
|
||||||
child.parent = value;
|
return value;
|
||||||
}
|
}
|
||||||
|
const [startOffset, startLine, startCol] = value.start;
|
||||||
|
const [endOffset, endLine, endCol] = value.end;
|
||||||
|
const range = new vscode.Range(startLine, startCol, endLine, endCol);
|
||||||
|
const offsets = {
|
||||||
|
start: startOffset,
|
||||||
|
end: endOffset,
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner;
|
||||||
|
if (value.istart && value.iend) {
|
||||||
|
const [istartOffset, istartLine, istartCol] = value.istart;
|
||||||
|
const [iendOffset, iendLine, iendCol] = value.iend;
|
||||||
|
|
||||||
|
inner = {
|
||||||
|
offsets: {
|
||||||
|
start: istartOffset,
|
||||||
|
end: iendOffset,
|
||||||
|
},
|
||||||
|
range: new vscode.Range(istartLine, istartCol, iendLine, iendCol),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
if (value.type === "Node") {
|
||||||
|
const result = {
|
||||||
|
type: value.type,
|
||||||
|
kind: value.kind,
|
||||||
|
offsets,
|
||||||
|
range,
|
||||||
|
inner,
|
||||||
|
children: value.children,
|
||||||
|
parent: undefined,
|
||||||
|
document: editor.document,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const child of result.children) {
|
||||||
|
child.parent = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: value.type,
|
||||||
|
kind: value.kind,
|
||||||
|
offsets,
|
||||||
|
range,
|
||||||
|
inner,
|
||||||
|
parent: undefined,
|
||||||
|
document: editor.document,
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.root = undefined;
|
this.root = undefined;
|
||||||
|
|
@ -90,14 +133,14 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
|
||||||
this._onDidChangeTreeData.fire();
|
this._onDidChangeTreeData.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
getElementByRange(start: number, end: number): SyntaxElement | undefined {
|
getElementByRange(target: vscode.Range): SyntaxElement | undefined {
|
||||||
if (this.root === undefined) {
|
if (this.root === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result: SyntaxElement = this.root;
|
let result: SyntaxElement = this.root;
|
||||||
|
|
||||||
if (this.root.start === start && this.root.end === end) {
|
if (this.root.range.isEqual(target)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,9 +148,9 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
|
||||||
|
|
||||||
outer: while (true) {
|
outer: while (true) {
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
if (child.start <= start && child.end >= end) {
|
if (child.range.contains(target)) {
|
||||||
result = child;
|
result = child;
|
||||||
if (start === end && start === child.end) {
|
if (target.isEmpty && target.start === child.range.end) {
|
||||||
// When the cursor is on the very end of a token,
|
// When the cursor is on the very end of a token,
|
||||||
// we assume the user wants the next token instead.
|
// we assume the user wants the next token instead.
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -136,31 +179,72 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
|
||||||
export type SyntaxNode = {
|
export type SyntaxNode = {
|
||||||
type: "Node";
|
type: "Node";
|
||||||
kind: string;
|
kind: string;
|
||||||
|
range: vscode.Range;
|
||||||
|
offsets: {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
istart?: number;
|
};
|
||||||
iend?: number;
|
/** This element's position within a Rust string literal, if it's inside of one. */
|
||||||
|
inner?: {
|
||||||
|
range: vscode.Range;
|
||||||
|
offsets: {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
children: SyntaxElement[];
|
children: SyntaxElement[];
|
||||||
parent?: SyntaxElement;
|
parent?: SyntaxElement;
|
||||||
|
document: vscode.TextDocument;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SyntaxToken = {
|
type SyntaxToken = {
|
||||||
type: "Token";
|
type: "Token";
|
||||||
kind: string;
|
kind: string;
|
||||||
|
range: vscode.Range;
|
||||||
|
offsets: {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
istart?: number;
|
};
|
||||||
iend?: number;
|
/** This element's position within a Rust string literal, if it's inside of one. */
|
||||||
|
inner?: {
|
||||||
|
range: vscode.Range;
|
||||||
|
offsets: {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
parent?: SyntaxElement;
|
parent?: SyntaxElement;
|
||||||
|
document: vscode.TextDocument;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SyntaxElement = SyntaxNode | SyntaxToken;
|
export type SyntaxElement = SyntaxNode | SyntaxToken;
|
||||||
|
|
||||||
|
type RawNode = {
|
||||||
|
type: "Node";
|
||||||
|
kind: string;
|
||||||
|
start: [number, number, number];
|
||||||
|
end: [number, number, number];
|
||||||
|
istart?: [number, number, number];
|
||||||
|
iend?: [number, number, number];
|
||||||
|
children: SyntaxElement[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type RawToken = {
|
||||||
|
type: "Token";
|
||||||
|
kind: string;
|
||||||
|
start: [number, number, number];
|
||||||
|
end: [number, number, number];
|
||||||
|
istart?: [number, number, number];
|
||||||
|
iend?: [number, number, number];
|
||||||
|
};
|
||||||
|
|
||||||
|
type RawElement = RawNode | RawToken;
|
||||||
|
|
||||||
export class SyntaxTreeItem extends vscode.TreeItem {
|
export class SyntaxTreeItem extends vscode.TreeItem {
|
||||||
constructor(private readonly element: SyntaxElement) {
|
constructor(private readonly element: SyntaxElement) {
|
||||||
super(element.kind);
|
super(element.kind);
|
||||||
const icon = getIcon(element.kind);
|
const icon = getIcon(this.element.kind);
|
||||||
if (element.type === "Node") {
|
if (this.element.type === "Node") {
|
||||||
this.contextValue = "syntaxNode";
|
this.contextValue = "syntaxNode";
|
||||||
this.iconPath = icon ?? new vscode.ThemeIcon("list-tree");
|
this.iconPath = icon ?? new vscode.ThemeIcon("list-tree");
|
||||||
this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
|
this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
|
||||||
|
|
@ -170,11 +254,9 @@ export class SyntaxTreeItem extends vscode.TreeItem {
|
||||||
this.collapsibleState = vscode.TreeItemCollapsibleState.None;
|
this.collapsibleState = vscode.TreeItemCollapsibleState.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.istart !== undefined && element.iend !== undefined) {
|
const offsets = this.element.inner?.offsets ?? this.element.offsets;
|
||||||
this.description = `${this.element.istart}..${this.element.iend}`;
|
|
||||||
} else {
|
this.description = `${offsets.start}..${offsets.end}`;
|
||||||
this.description = `${this.element.start}..${this.element.end}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue