Feat: updates to docs, extension
This commit is contained in:
1 parent
2e626aea51
commit
a2406b33d6
25 files changed
+917
-508
No files matched your search
@@ -1,6 +1,6 @@
|
||||
use std::future::Future;
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core::{prelude::*, virtual_dom::Properties};
|
||||
// use virtual_dom_rs::Closure;
|
||||
|
||||
// Stop-gap while developing
|
||||
@@ -13,9 +13,11 @@ pub fn main() {
|
||||
// let output = renderer.render();
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Props {
|
||||
name: String,
|
||||
}
|
||||
impl Properties for Props {}
|
||||
|
||||
fn root(ctx: &mut Context<Props>) -> VNode {
|
||||
// the regular html syntax
|
||||
@@ -39,10 +41,17 @@ fn root(ctx: &mut Context<Props>) -> VNode {
|
||||
}
|
||||
|
||||
let mut node_1: IterableNodes = ("Hello world!").into();
|
||||
|
||||
node_1.first().insert_space_before_text();
|
||||
let mut node_2 = VNode::element("button");
|
||||
|
||||
let node_3 = VNode::Component(VComponent {});
|
||||
let node_3 = VNode::Component(VComponent::from_fn(
|
||||
Head,
|
||||
Props {
|
||||
name: "".to_string(),
|
||||
},
|
||||
));
|
||||
|
||||
{
|
||||
// let closure = Closure::wrap(Box::new(|_| {}) as Box<FnMut(_)>);
|
||||
// let closure_rc = std::rc::Rc::new(closure);
|
||||
@@ -67,17 +76,13 @@ fn root(ctx: &mut Context<Props>) -> VNode {
|
||||
|
||||
fn Head(ctx: &mut Context<Props>) -> VNode {
|
||||
html! {
|
||||
<head>
|
||||
"Head Section"
|
||||
</head>
|
||||
<head> "Head Section" </head>
|
||||
}
|
||||
}
|
||||
|
||||
fn Body(ctx: &mut Context<Props>) -> VNode {
|
||||
html! {
|
||||
<body>
|
||||
{"Footer Section"}
|
||||
</body>
|
||||
<body> {"Footer Section"}</body>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+78
-11
@@ -6,8 +6,6 @@
|
||||
//! </div>
|
||||
//! Dioxus: a concurrent, functional, reactive virtual dom for any renderer in Rust.
|
||||
//!
|
||||
//!
|
||||
//! Dioxus is an efficient virtual dom implementation for building interactive user interfaces in Rust.
|
||||
//! This crate aims to maintain a uniform hook-based, renderer-agnostic UI framework for cross-platform development.
|
||||
//!
|
||||
//! ## Components
|
||||
@@ -15,14 +13,38 @@
|
||||
//! ```
|
||||
//! use dioxus_core::prelude::*;
|
||||
//!
|
||||
//! fn Example(ctx: Context<()>) -> VNode {
|
||||
//! html! { <div> "Hello world!" </div> }
|
||||
//! #[derive(Properties)]
|
||||
//! struct Props { name: String }
|
||||
//!
|
||||
//! fn Example(ctx: &mut Context<Props>) -> VNode {
|
||||
//! html! { <div> "Hello {ctx.props.name}!" </div> }
|
||||
//! }
|
||||
//! ```
|
||||
//! Components need to take a "Context" parameter which is generic over some properties. This defines how the component can be used
|
||||
//! and what properties can be used to specify it in the VNode output. All components in Dioxus are hook-based, which might be more
|
||||
//! complex than other approaches that use traits + lifecycle events. Alternatively, we provide a "lifecycle hook" if you want more
|
||||
//! granualar control with behavior similar to other UI frameworks.
|
||||
//! and what properties can be used to specify it in the VNode output. Component state in Dioxus is managed by hooks - if you're new
|
||||
//! to hooks, check out the hook guide in the official guide.
|
||||
//!
|
||||
//! Components can also be crafted as static closures, enabling type inference without all the type signature noise:
|
||||
//! ```
|
||||
//! use dioxus_core::prelude::*;
|
||||
//!
|
||||
//! #[derive(Properties)]
|
||||
//! struct Props { name: String }
|
||||
//!
|
||||
//! static Example: FC<Props> = |ctx| {
|
||||
//! html! { <div> "Hello {ctx.props.name}!" </div> }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! If the properties struct is too noisy for you, we also provide a macro that converts variadic functions into components automatically.
|
||||
//! ```
|
||||
//! use dioxus_core::prelude::*;
|
||||
//!
|
||||
//! #[functional_component]
|
||||
//! static Example: FC = |ctx, name: String| {
|
||||
//! html! { <div> "Hello {name}!" </div> }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Hooks
|
||||
//! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component. Instead of
|
||||
@@ -177,7 +199,12 @@ pub mod virtual_dom {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Properties {}
|
||||
pub trait Properties: PartialEq + 'static {
|
||||
/*
|
||||
Props cannot contain references (as of today) :(
|
||||
Use a smart pointer
|
||||
*/
|
||||
}
|
||||
|
||||
// Auto derive for pure components
|
||||
impl Properties for () {}
|
||||
@@ -187,8 +214,9 @@ pub mod virtual_dom {
|
||||
}
|
||||
|
||||
/// Virtual Node Support
|
||||
/// VNodes represent lazily-constructed VDom trees that support diffing and event handlers.
|
||||
///
|
||||
///
|
||||
/// These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
||||
///
|
||||
pub mod nodes {
|
||||
pub use vcomponent::VComponent;
|
||||
@@ -528,13 +556,52 @@ pub mod nodes {
|
||||
/// Virtual Components for custom user-defined components
|
||||
/// Only supports the functional syntax
|
||||
mod vcomponent {
|
||||
use crate::{
|
||||
prelude::{Context, FC},
|
||||
virtual_dom::Properties,
|
||||
};
|
||||
use std::{any::TypeId, fmt, future::Future};
|
||||
|
||||
use super::VNode;
|
||||
#[derive(PartialEq)]
|
||||
pub struct VComponent {}
|
||||
pub struct VComponent {
|
||||
props_id: TypeId,
|
||||
// callerIDs are unsafely coerced to function pointers
|
||||
// This is okay because #1, we store the props_id and verify and 2# the html! macro rejects components not made this way
|
||||
//
|
||||
// Manually constructing the VComponent is not possible from 3rd party crates
|
||||
}
|
||||
|
||||
impl VComponent {
|
||||
/// Construct a VComponent directly from a function component
|
||||
/// This should be *very* fast - we store the function pointer and props type ID. It should also be small on the stack
|
||||
pub fn from_fn<P: Properties>(f: FC<P>, props: P) -> Self {
|
||||
// Props needs to be static
|
||||
let props_id = std::any::TypeId::of::<P>();
|
||||
|
||||
// Cast the caller down
|
||||
|
||||
Self { props_id }
|
||||
}
|
||||
|
||||
/// Construct a VComponent directly from a concurrent component
|
||||
pub fn from_asyncfn<P: Properties, F>(f: fn(&mut Context<P>) -> F, props: P) -> Self
|
||||
where
|
||||
F: Future<Output = VNode>,
|
||||
{
|
||||
// Props needs to be static
|
||||
let props_id = std::any::TypeId::of::<P>();
|
||||
|
||||
// Cast the caller down
|
||||
|
||||
Self { props_id }
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// Allow debug/display adherent to the HTML spec
|
||||
// -----------------------------------------------
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Debug for VComponent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO: @JON Implement how components should be formatted when spit out to html
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let mut s = Context { props: &() };
|
||||
let g = Component(&mut s);
|
||||
}
|
||||
|
||||
struct CompState {
|
||||
tasks: Vec<()>,
|
||||
}
|
||||
enum Actions {
|
||||
Add,
|
||||
MoveUp,
|
||||
MoveDown,
|
||||
Remvoe,
|
||||
}
|
||||
|
||||
static Component: FC<()> = |ctx| {
|
||||
let (tasks, dispatch) = use_reducer(
|
||||
ctx,
|
||||
|| CompState { tasks: Vec::new() },
|
||||
|state, action: Actions| match action {
|
||||
Actions::Add => state,
|
||||
Actions::MoveUp => state,
|
||||
Actions::MoveDown => state,
|
||||
Actions::Remvoe => state,
|
||||
},
|
||||
);
|
||||
|
||||
let tasklist = { (0..10).map(|f| html! { <li></li> }) }.collect::<Vec<_>>();
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<div>
|
||||
<h1>"Tasks: "</h1>
|
||||
<ul>
|
||||
{tasklist}
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick=|_| dispatch(Action::Add)>{"Add"}</button>
|
||||
<button onclick=|_| dispatch(Action::MoveUp)>{"MoveUp"}</button>
|
||||
<button onclick=|_| dispatch(Action::MoveDown)>{"MoveDown"}</button>
|
||||
<button onclick=|_| dispatch(Action::Remvoe)>{"Remvoe"}</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
|
||||
fn use_reducer<Props, State, Action>(
|
||||
ctx: &mut Context<Props>,
|
||||
init: fn() -> State,
|
||||
reducer: fn(State, Action) -> State,
|
||||
) -> (State, impl Fn(Action)) {
|
||||
let ii = init();
|
||||
(ii, |_| {})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "dioxus-livehost"
|
||||
version = "0.0.0"
|
||||
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
+5
@@ -57,6 +57,11 @@
|
||||
"vscode-languageserver-types": "3.15.1"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-textdocument": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz",
|
||||
"integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA=="
|
||||
},
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.15.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-html-languageservice": "^3.0.3",
|
||||
"vscode-languageclient": "^6.1.3"
|
||||
"vscode-languageclient": "^6.1.3",
|
||||
"vscode-languageserver-textdocument": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "^1.43.0"
|
||||
|
||||
@@ -3,413 +3,447 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument, Position, Range } from 'vscode-languageclient';
|
||||
import { LanguageService, TokenType } from 'vscode-html-languageservice';
|
||||
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||
import { Position, Range } from "vscode-languageclient";
|
||||
import { LanguageService, TokenType } from "vscode-html-languageservice";
|
||||
|
||||
export interface LanguageRange extends Range {
|
||||
languageId: string | undefined;
|
||||
attributeValue?: boolean;
|
||||
languageId: string | undefined;
|
||||
attributeValue?: boolean;
|
||||
}
|
||||
|
||||
export interface HTMLDocumentRegions {
|
||||
getEmbeddedDocument(languageId: string, ignoreAttributeValues?: boolean): TextDocument;
|
||||
getLanguageRanges(range: Range): LanguageRange[];
|
||||
getLanguageAtPosition(position: Position): string | undefined;
|
||||
getLanguagesInDocument(): string[];
|
||||
getImportedScripts(): string[];
|
||||
getEmbeddedDocument(
|
||||
languageId: string,
|
||||
ignoreAttributeValues?: boolean
|
||||
): TextDocument;
|
||||
getLanguageRanges(range: Range): LanguageRange[];
|
||||
getLanguageAtPosition(position: Position): string | undefined;
|
||||
getLanguagesInDocument(): string[];
|
||||
getImportedScripts(): string[];
|
||||
}
|
||||
|
||||
export const CSS_STYLE_RULE = '__';
|
||||
export const CSS_STYLE_RULE = "__";
|
||||
|
||||
interface EmbeddedRegion {
|
||||
languageId: string | undefined;
|
||||
start: number;
|
||||
end: number;
|
||||
attributeValue?: boolean;
|
||||
languageId: string | undefined;
|
||||
start: number;
|
||||
end: number;
|
||||
attributeValue?: boolean;
|
||||
}
|
||||
|
||||
// Check if the request is coming from inside a special region
|
||||
export function isInsideStyleRegion(
|
||||
languageService: LanguageService,
|
||||
documentText: string,
|
||||
offset: number
|
||||
languageService: LanguageService,
|
||||
documentText: string,
|
||||
offset: number
|
||||
) {
|
||||
let scanner = languageService.createScanner(documentText);
|
||||
let scanner = languageService.createScanner(documentText);
|
||||
|
||||
let token = scanner.scan();
|
||||
while (token !== TokenType.EOS) {
|
||||
switch (token) {
|
||||
case TokenType.Styles:
|
||||
if (offset >= scanner.getTokenOffset() && offset <= scanner.getTokenEnd()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
|
||||
return false;
|
||||
let token = scanner.scan();
|
||||
while (token !== TokenType.EOS) {
|
||||
switch (token) {
|
||||
case TokenType.Styles:
|
||||
if (
|
||||
offset >= scanner.getTokenOffset() &&
|
||||
offset <= scanner.getTokenEnd()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getCSSVirtualContent(
|
||||
languageService: LanguageService,
|
||||
documentText: string
|
||||
languageService: LanguageService,
|
||||
documentText: string
|
||||
): string {
|
||||
let regions: EmbeddedRegion[] = [];
|
||||
let scanner = languageService.createScanner(documentText);
|
||||
let lastTagName: string = '';
|
||||
let lastAttributeName: string | null = null;
|
||||
let languageIdFromType: string | undefined = undefined;
|
||||
let importedScripts: string[] = [];
|
||||
let regions: EmbeddedRegion[] = [];
|
||||
let scanner = languageService.createScanner(documentText);
|
||||
let lastTagName: string = "";
|
||||
let lastAttributeName: string | null = null;
|
||||
let languageIdFromType: string | undefined = undefined;
|
||||
let importedScripts: string[] = [];
|
||||
|
||||
let token = scanner.scan();
|
||||
while (token !== TokenType.EOS) {
|
||||
switch (token) {
|
||||
case TokenType.StartTag:
|
||||
lastTagName = scanner.getTokenText();
|
||||
lastAttributeName = null;
|
||||
languageIdFromType = 'javascript';
|
||||
break;
|
||||
case TokenType.Styles:
|
||||
regions.push({
|
||||
languageId: 'css',
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd()
|
||||
});
|
||||
break;
|
||||
case TokenType.Script:
|
||||
regions.push({
|
||||
languageId: languageIdFromType,
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd()
|
||||
});
|
||||
break;
|
||||
case TokenType.AttributeName:
|
||||
lastAttributeName = scanner.getTokenText();
|
||||
break;
|
||||
case TokenType.AttributeValue:
|
||||
if (lastAttributeName === 'src' && lastTagName.toLowerCase() === 'script') {
|
||||
let value = scanner.getTokenText();
|
||||
if (value[0] === "'" || value[0] === '"') {
|
||||
value = value.substr(1, value.length - 1);
|
||||
}
|
||||
importedScripts.push(value);
|
||||
} else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') {
|
||||
if (
|
||||
/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(
|
||||
scanner.getTokenText()
|
||||
)
|
||||
) {
|
||||
languageIdFromType = 'javascript';
|
||||
} else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
||||
languageIdFromType = 'typescript';
|
||||
} else {
|
||||
languageIdFromType = undefined;
|
||||
}
|
||||
} else {
|
||||
let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
||||
if (attributeLanguageId) {
|
||||
let start = scanner.getTokenOffset();
|
||||
let end = scanner.getTokenEnd();
|
||||
let firstChar = documentText[start];
|
||||
if (firstChar === "'" || firstChar === '"') {
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
regions.push({
|
||||
languageId: attributeLanguageId,
|
||||
start,
|
||||
end,
|
||||
attributeValue: true
|
||||
});
|
||||
}
|
||||
}
|
||||
lastAttributeName = null;
|
||||
break;
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
let token = scanner.scan();
|
||||
while (token !== TokenType.EOS) {
|
||||
switch (token) {
|
||||
case TokenType.StartTag:
|
||||
lastTagName = scanner.getTokenText();
|
||||
lastAttributeName = null;
|
||||
languageIdFromType = "javascript";
|
||||
break;
|
||||
case TokenType.Styles:
|
||||
regions.push({
|
||||
languageId: "css",
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd(),
|
||||
});
|
||||
break;
|
||||
case TokenType.Script:
|
||||
regions.push({
|
||||
languageId: languageIdFromType,
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd(),
|
||||
});
|
||||
break;
|
||||
case TokenType.AttributeName:
|
||||
lastAttributeName = scanner.getTokenText();
|
||||
break;
|
||||
case TokenType.AttributeValue:
|
||||
if (
|
||||
lastAttributeName === "src" &&
|
||||
lastTagName.toLowerCase() === "script"
|
||||
) {
|
||||
let value = scanner.getTokenText();
|
||||
if (value[0] === "'" || value[0] === '"') {
|
||||
value = value.substr(1, value.length - 1);
|
||||
}
|
||||
importedScripts.push(value);
|
||||
} else if (
|
||||
lastAttributeName === "type" &&
|
||||
lastTagName.toLowerCase() === "script"
|
||||
) {
|
||||
if (
|
||||
/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(
|
||||
scanner.getTokenText()
|
||||
)
|
||||
) {
|
||||
languageIdFromType = "javascript";
|
||||
} else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
||||
languageIdFromType = "typescript";
|
||||
} else {
|
||||
languageIdFromType = undefined;
|
||||
}
|
||||
} else {
|
||||
let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
||||
if (attributeLanguageId) {
|
||||
let start = scanner.getTokenOffset();
|
||||
let end = scanner.getTokenEnd();
|
||||
let firstChar = documentText[start];
|
||||
if (firstChar === "'" || firstChar === '"') {
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
regions.push({
|
||||
languageId: attributeLanguageId,
|
||||
start,
|
||||
end,
|
||||
attributeValue: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
lastAttributeName = null;
|
||||
break;
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
|
||||
let content = documentText
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
return ' '.repeat(line.length);
|
||||
}).join('\n');
|
||||
let content = documentText
|
||||
.split("\n")
|
||||
.map((line) => {
|
||||
return " ".repeat(line.length);
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
regions.forEach(r => {
|
||||
if (r.languageId === 'css') {
|
||||
content = content.slice(0, r.start) + documentText.slice(r.start, r.end) + content.slice(r.end);
|
||||
}
|
||||
});
|
||||
regions.forEach((r) => {
|
||||
if (r.languageId === "css") {
|
||||
content =
|
||||
content.slice(0, r.start) +
|
||||
documentText.slice(r.start, r.end) +
|
||||
content.slice(r.end);
|
||||
}
|
||||
});
|
||||
|
||||
return content;
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
export function getDocumentRegions(
|
||||
languageService: LanguageService,
|
||||
document: TextDocument
|
||||
languageService: LanguageService,
|
||||
document: TextDocument
|
||||
): HTMLDocumentRegions {
|
||||
let regions: EmbeddedRegion[] = [];
|
||||
let scanner = languageService.createScanner(document.getText());
|
||||
let lastTagName: string = '';
|
||||
let lastAttributeName: string | null = null;
|
||||
let languageIdFromType: string | undefined = undefined;
|
||||
let importedScripts: string[] = [];
|
||||
let regions: EmbeddedRegion[] = [];
|
||||
let scanner = languageService.createScanner(document.getText());
|
||||
let lastTagName: string = "";
|
||||
let lastAttributeName: string | null = null;
|
||||
let languageIdFromType: string | undefined = undefined;
|
||||
let importedScripts: string[] = [];
|
||||
|
||||
let token = scanner.scan();
|
||||
while (token !== TokenType.EOS) {
|
||||
switch (token) {
|
||||
case TokenType.StartTag:
|
||||
lastTagName = scanner.getTokenText();
|
||||
lastAttributeName = null;
|
||||
languageIdFromType = 'javascript';
|
||||
break;
|
||||
case TokenType.Styles:
|
||||
regions.push({
|
||||
languageId: 'css',
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd()
|
||||
});
|
||||
break;
|
||||
case TokenType.Script:
|
||||
regions.push({
|
||||
languageId: languageIdFromType,
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd()
|
||||
});
|
||||
break;
|
||||
case TokenType.AttributeName:
|
||||
lastAttributeName = scanner.getTokenText();
|
||||
break;
|
||||
case TokenType.AttributeValue:
|
||||
if (lastAttributeName === 'src' && lastTagName.toLowerCase() === 'script') {
|
||||
let value = scanner.getTokenText();
|
||||
if (value[0] === "'" || value[0] === '"') {
|
||||
value = value.substr(1, value.length - 1);
|
||||
}
|
||||
importedScripts.push(value);
|
||||
} else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') {
|
||||
if (
|
||||
/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(
|
||||
scanner.getTokenText()
|
||||
)
|
||||
) {
|
||||
languageIdFromType = 'javascript';
|
||||
} else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
||||
languageIdFromType = 'typescript';
|
||||
} else {
|
||||
languageIdFromType = undefined;
|
||||
}
|
||||
} else {
|
||||
let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
||||
if (attributeLanguageId) {
|
||||
let start = scanner.getTokenOffset();
|
||||
let end = scanner.getTokenEnd();
|
||||
let firstChar = document.getText()[start];
|
||||
if (firstChar === "'" || firstChar === '"') {
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
regions.push({
|
||||
languageId: attributeLanguageId,
|
||||
start,
|
||||
end,
|
||||
attributeValue: true
|
||||
});
|
||||
}
|
||||
}
|
||||
lastAttributeName = null;
|
||||
break;
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
return {
|
||||
getLanguageRanges: (range: Range) => getLanguageRanges(document, regions, range),
|
||||
getEmbeddedDocument: (languageId: string, ignoreAttributeValues: boolean) =>
|
||||
getEmbeddedDocument(document, regions, languageId, ignoreAttributeValues),
|
||||
getLanguageAtPosition: (position: Position) =>
|
||||
getLanguageAtPosition(document, regions, position),
|
||||
getLanguagesInDocument: () => getLanguagesInDocument(document, regions),
|
||||
getImportedScripts: () => importedScripts
|
||||
};
|
||||
let token = scanner.scan();
|
||||
while (token !== TokenType.EOS) {
|
||||
switch (token) {
|
||||
case TokenType.StartTag:
|
||||
lastTagName = scanner.getTokenText();
|
||||
lastAttributeName = null;
|
||||
languageIdFromType = "javascript";
|
||||
break;
|
||||
case TokenType.Styles:
|
||||
regions.push({
|
||||
languageId: "css",
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd(),
|
||||
});
|
||||
break;
|
||||
case TokenType.Script:
|
||||
regions.push({
|
||||
languageId: languageIdFromType,
|
||||
start: scanner.getTokenOffset(),
|
||||
end: scanner.getTokenEnd(),
|
||||
});
|
||||
break;
|
||||
case TokenType.AttributeName:
|
||||
lastAttributeName = scanner.getTokenText();
|
||||
break;
|
||||
case TokenType.AttributeValue:
|
||||
if (
|
||||
lastAttributeName === "src" &&
|
||||
lastTagName.toLowerCase() === "script"
|
||||
) {
|
||||
let value = scanner.getTokenText();
|
||||
if (value[0] === "'" || value[0] === '"') {
|
||||
value = value.substr(1, value.length - 1);
|
||||
}
|
||||
importedScripts.push(value);
|
||||
} else if (
|
||||
lastAttributeName === "type" &&
|
||||
lastTagName.toLowerCase() === "script"
|
||||
) {
|
||||
if (
|
||||
/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(
|
||||
scanner.getTokenText()
|
||||
)
|
||||
) {
|
||||
languageIdFromType = "javascript";
|
||||
} else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
||||
languageIdFromType = "typescript";
|
||||
} else {
|
||||
languageIdFromType = undefined;
|
||||
}
|
||||
} else {
|
||||
let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
||||
if (attributeLanguageId) {
|
||||
let start = scanner.getTokenOffset();
|
||||
let end = scanner.getTokenEnd();
|
||||
let firstChar = document.getText()[start];
|
||||
if (firstChar === "'" || firstChar === '"') {
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
regions.push({
|
||||
languageId: attributeLanguageId,
|
||||
start,
|
||||
end,
|
||||
attributeValue: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
lastAttributeName = null;
|
||||
break;
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
return {
|
||||
getLanguageRanges: (range: Range) =>
|
||||
getLanguageRanges(document, regions, range),
|
||||
getEmbeddedDocument: (languageId: string, ignoreAttributeValues: boolean) =>
|
||||
getEmbeddedDocument(document, regions, languageId, ignoreAttributeValues),
|
||||
getLanguageAtPosition: (position: Position) =>
|
||||
getLanguageAtPosition(document, regions, position),
|
||||
getLanguagesInDocument: () => getLanguagesInDocument(document, regions),
|
||||
getImportedScripts: () => importedScripts,
|
||||
};
|
||||
}
|
||||
|
||||
function getLanguageRanges(
|
||||
document: TextDocument,
|
||||
regions: EmbeddedRegion[],
|
||||
range: Range
|
||||
document: TextDocument,
|
||||
regions: EmbeddedRegion[],
|
||||
range: Range
|
||||
): LanguageRange[] {
|
||||
let result: LanguageRange[] = [];
|
||||
let currentPos = range ? range.start : Position.create(0, 0);
|
||||
let currentOffset = range ? document.offsetAt(range.start) : 0;
|
||||
let endOffset = range ? document.offsetAt(range.end) : document.getText().length;
|
||||
for (let region of regions) {
|
||||
if (region.end > currentOffset && region.start < endOffset) {
|
||||
let start = Math.max(region.start, currentOffset);
|
||||
let startPos = document.positionAt(start);
|
||||
if (currentOffset < region.start) {
|
||||
result.push({
|
||||
start: currentPos,
|
||||
end: startPos,
|
||||
languageId: 'html'
|
||||
});
|
||||
}
|
||||
let end = Math.min(region.end, endOffset);
|
||||
let endPos = document.positionAt(end);
|
||||
if (end > region.start) {
|
||||
result.push({
|
||||
start: startPos,
|
||||
end: endPos,
|
||||
languageId: region.languageId,
|
||||
attributeValue: region.attributeValue
|
||||
});
|
||||
}
|
||||
currentOffset = end;
|
||||
currentPos = endPos;
|
||||
}
|
||||
}
|
||||
if (currentOffset < endOffset) {
|
||||
let endPos = range ? range.end : document.positionAt(endOffset);
|
||||
result.push({
|
||||
start: currentPos,
|
||||
end: endPos,
|
||||
languageId: 'html'
|
||||
});
|
||||
}
|
||||
return result;
|
||||
let result: LanguageRange[] = [];
|
||||
let currentPos = range ? range.start : Position.create(0, 0);
|
||||
let currentOffset = range ? document.offsetAt(range.start) : 0;
|
||||
let endOffset = range
|
||||
? document.offsetAt(range.end)
|
||||
: document.getText().length;
|
||||
for (let region of regions) {
|
||||
if (region.end > currentOffset && region.start < endOffset) {
|
||||
let start = Math.max(region.start, currentOffset);
|
||||
let startPos = document.positionAt(start);
|
||||
if (currentOffset < region.start) {
|
||||
result.push({
|
||||
start: currentPos,
|
||||
end: startPos,
|
||||
languageId: "html",
|
||||
});
|
||||
}
|
||||
let end = Math.min(region.end, endOffset);
|
||||
let endPos = document.positionAt(end);
|
||||
if (end > region.start) {
|
||||
result.push({
|
||||
start: startPos,
|
||||
end: endPos,
|
||||
languageId: region.languageId,
|
||||
attributeValue: region.attributeValue,
|
||||
});
|
||||
}
|
||||
currentOffset = end;
|
||||
currentPos = endPos;
|
||||
}
|
||||
}
|
||||
if (currentOffset < endOffset) {
|
||||
let endPos = range ? range.end : document.positionAt(endOffset);
|
||||
result.push({
|
||||
start: currentPos,
|
||||
end: endPos,
|
||||
languageId: "html",
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getLanguagesInDocument(
|
||||
_document: TextDocument,
|
||||
regions: EmbeddedRegion[]
|
||||
_document: TextDocument,
|
||||
regions: EmbeddedRegion[]
|
||||
): string[] {
|
||||
let result = [];
|
||||
for (let region of regions) {
|
||||
if (region.languageId && result.indexOf(region.languageId) === -1) {
|
||||
result.push(region.languageId);
|
||||
if (result.length === 3) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push('html');
|
||||
return result;
|
||||
let result = [];
|
||||
for (let region of regions) {
|
||||
if (region.languageId && result.indexOf(region.languageId) === -1) {
|
||||
result.push(region.languageId);
|
||||
if (result.length === 3) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push("html");
|
||||
return result;
|
||||
}
|
||||
|
||||
function getLanguageAtPosition(
|
||||
document: TextDocument,
|
||||
regions: EmbeddedRegion[],
|
||||
position: Position
|
||||
document: TextDocument,
|
||||
regions: EmbeddedRegion[],
|
||||
position: Position
|
||||
): string | undefined {
|
||||
let offset = document.offsetAt(position);
|
||||
for (let region of regions) {
|
||||
if (region.start <= offset) {
|
||||
if (offset <= region.end) {
|
||||
return region.languageId;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 'html';
|
||||
let offset = document.offsetAt(position);
|
||||
for (let region of regions) {
|
||||
if (region.start <= offset) {
|
||||
if (offset <= region.end) {
|
||||
return region.languageId;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "html";
|
||||
}
|
||||
|
||||
function getEmbeddedDocument(
|
||||
document: TextDocument,
|
||||
contents: EmbeddedRegion[],
|
||||
languageId: string,
|
||||
ignoreAttributeValues: boolean
|
||||
document: TextDocument,
|
||||
contents: EmbeddedRegion[],
|
||||
languageId: string,
|
||||
ignoreAttributeValues: boolean
|
||||
): TextDocument {
|
||||
let currentPos = 0;
|
||||
let oldContent = document.getText();
|
||||
let result = '';
|
||||
let lastSuffix = '';
|
||||
for (let c of contents) {
|
||||
if (c.languageId === languageId && (!ignoreAttributeValues || !c.attributeValue)) {
|
||||
result = substituteWithWhitespace(
|
||||
result,
|
||||
currentPos,
|
||||
c.start,
|
||||
oldContent,
|
||||
lastSuffix,
|
||||
getPrefix(c)
|
||||
);
|
||||
result += oldContent.substring(c.start, c.end);
|
||||
currentPos = c.end;
|
||||
lastSuffix = getSuffix(c);
|
||||
}
|
||||
}
|
||||
result = substituteWithWhitespace(
|
||||
result,
|
||||
currentPos,
|
||||
oldContent.length,
|
||||
oldContent,
|
||||
lastSuffix,
|
||||
''
|
||||
);
|
||||
return TextDocument.create(document.uri, languageId, document.version, result);
|
||||
let currentPos = 0;
|
||||
let oldContent = document.getText();
|
||||
let result = "";
|
||||
let lastSuffix = "";
|
||||
for (let c of contents) {
|
||||
if (
|
||||
c.languageId === languageId &&
|
||||
(!ignoreAttributeValues || !c.attributeValue)
|
||||
) {
|
||||
result = substituteWithWhitespace(
|
||||
result,
|
||||
currentPos,
|
||||
c.start,
|
||||
oldContent,
|
||||
lastSuffix,
|
||||
getPrefix(c)
|
||||
);
|
||||
result += oldContent.substring(c.start, c.end);
|
||||
currentPos = c.end;
|
||||
lastSuffix = getSuffix(c);
|
||||
}
|
||||
}
|
||||
result = substituteWithWhitespace(
|
||||
result,
|
||||
currentPos,
|
||||
oldContent.length,
|
||||
oldContent,
|
||||
lastSuffix,
|
||||
""
|
||||
);
|
||||
return TextDocument.create(
|
||||
document.uri,
|
||||
languageId,
|
||||
document.version,
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
function getPrefix(c: EmbeddedRegion) {
|
||||
if (c.attributeValue) {
|
||||
switch (c.languageId) {
|
||||
case 'css':
|
||||
return CSS_STYLE_RULE + '{';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
if (c.attributeValue) {
|
||||
switch (c.languageId) {
|
||||
case "css":
|
||||
return CSS_STYLE_RULE + "{";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
function getSuffix(c: EmbeddedRegion) {
|
||||
if (c.attributeValue) {
|
||||
switch (c.languageId) {
|
||||
case 'css':
|
||||
return '}';
|
||||
case 'javascript':
|
||||
return ';';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
if (c.attributeValue) {
|
||||
switch (c.languageId) {
|
||||
case "css":
|
||||
return "}";
|
||||
case "javascript":
|
||||
return ";";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function substituteWithWhitespace(
|
||||
result: string,
|
||||
start: number,
|
||||
end: number,
|
||||
oldContent: string,
|
||||
before: string,
|
||||
after: string
|
||||
result: string,
|
||||
start: number,
|
||||
end: number,
|
||||
oldContent: string,
|
||||
before: string,
|
||||
after: string
|
||||
) {
|
||||
let accumulatedWS = 0;
|
||||
result += before;
|
||||
for (let i = start + before.length; i < end; i++) {
|
||||
let ch = oldContent[i];
|
||||
if (ch === '\n' || ch === '\r') {
|
||||
// only write new lines, skip the whitespace
|
||||
accumulatedWS = 0;
|
||||
result += ch;
|
||||
} else {
|
||||
accumulatedWS++;
|
||||
}
|
||||
}
|
||||
result = append(result, ' ', accumulatedWS - after.length);
|
||||
result += after;
|
||||
return result;
|
||||
let accumulatedWS = 0;
|
||||
result += before;
|
||||
for (let i = start + before.length; i < end; i++) {
|
||||
let ch = oldContent[i];
|
||||
if (ch === "\n" || ch === "\r") {
|
||||
// only write new lines, skip the whitespace
|
||||
accumulatedWS = 0;
|
||||
result += ch;
|
||||
} else {
|
||||
accumulatedWS++;
|
||||
}
|
||||
}
|
||||
result = append(result, " ", accumulatedWS - after.length);
|
||||
result += after;
|
||||
return result;
|
||||
}
|
||||
|
||||
function append(result: string, str: string, n: number): string {
|
||||
while (n > 0) {
|
||||
if (n & 1) {
|
||||
result += str;
|
||||
}
|
||||
n >>= 1;
|
||||
str += str;
|
||||
}
|
||||
return result;
|
||||
while (n > 0) {
|
||||
if (n & 1) {
|
||||
result += str;
|
||||
}
|
||||
n >>= 1;
|
||||
str += str;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAttributeLanguage(attributeName: string): string | null {
|
||||
let match = attributeName.match(/^(style)$|^(on\w+)$/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return match[1] ? 'css' : 'javascript';
|
||||
let match = attributeName.match(/^(style)$|^(on\w+)$/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return match[1] ? "css" : "javascript";
|
||||
}
|
||||
@@ -3,85 +3,145 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as path from 'path';
|
||||
import { commands, CompletionList, ExtensionContext, Uri, workspace } from 'vscode';
|
||||
import { getLanguageService } from 'vscode-html-languageservice';
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
|
||||
import { getCSSVirtualContent, isInsideStyleRegion } from './embeddedSupport';
|
||||
import * as path from "path";
|
||||
import {
|
||||
commands,
|
||||
CompletionList,
|
||||
ExtensionContext,
|
||||
Uri,
|
||||
workspace,
|
||||
} from "vscode";
|
||||
import { getLanguageService } from "vscode-html-languageservice";
|
||||
import {
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
ServerOptions,
|
||||
TransportKind,
|
||||
} from "vscode-languageclient";
|
||||
import { isInsideHtmlMacro } from "./rustSupport";
|
||||
// import { getCSSVirtualContent, isInsideStyleRegion } from "./embeddedSupport";
|
||||
|
||||
let client: LanguageClient;
|
||||
|
||||
const htmlLanguageService = getLanguageService();
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));
|
||||
// The debug options for the server
|
||||
// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
|
||||
let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] };
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(
|
||||
path.join("server", "out", "server.js")
|
||||
);
|
||||
// The debug options for the server
|
||||
// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
|
||||
let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] };
|
||||
|
||||
// If the extension is launched in debug mode then the debug server options are used
|
||||
// Otherwise the run options are used
|
||||
let serverOptions: ServerOptions = {
|
||||
run: { module: serverModule, transport: TransportKind.ipc },
|
||||
debug: {
|
||||
module: serverModule,
|
||||
transport: TransportKind.ipc,
|
||||
options: debugOptions
|
||||
}
|
||||
};
|
||||
// If the extension is launched in debug mode then the debug server options are used
|
||||
// Otherwise the run options are used
|
||||
let serverOptions: ServerOptions = {
|
||||
run: { module: serverModule, transport: TransportKind.ipc },
|
||||
debug: {
|
||||
module: serverModule,
|
||||
transport: TransportKind.ipc,
|
||||
options: debugOptions,
|
||||
},
|
||||
};
|
||||
|
||||
const virtualDocumentContents = new Map<string, string>();
|
||||
const virtualDocumentContents = new Map<string, string>();
|
||||
|
||||
workspace.registerTextDocumentContentProvider('embedded-content', {
|
||||
provideTextDocumentContent: uri => {
|
||||
const originalUri = uri.path.slice(1).slice(0, -4);
|
||||
const decodedUri = decodeURIComponent(originalUri);
|
||||
return virtualDocumentContents.get(decodedUri);
|
||||
}
|
||||
});
|
||||
workspace.registerTextDocumentContentProvider("embedded-content", {
|
||||
provideTextDocumentContent: (uri) => {
|
||||
const originalUri = uri.path.slice(1).slice(0, -4);
|
||||
console.error(originalUri);
|
||||
const decodedUri = decodeURIComponent(originalUri);
|
||||
return virtualDocumentContents.get(decodedUri);
|
||||
},
|
||||
});
|
||||
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: 'file', language: 'html1' }],
|
||||
middleware: {
|
||||
provideCompletionItem: async (document, position, context, token, next) => {
|
||||
// If not in `<style>`, do not perform request forwarding
|
||||
if (!isInsideStyleRegion(htmlLanguageService, document.getText(), document.offsetAt(position))) {
|
||||
return await next(document, position, context, token);
|
||||
}
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: "file", language: "rust" }],
|
||||
middleware: {
|
||||
provideCompletionItem: async (
|
||||
document,
|
||||
position,
|
||||
context,
|
||||
token,
|
||||
next
|
||||
) => {
|
||||
/*
|
||||
1: Find the occurences of the html! macro using regex
|
||||
2: Check if any of the occurences match the cursor offset
|
||||
3: If so, direct the captured block to the html to the rsx language service
|
||||
*/
|
||||
const docSrc = document.getText();
|
||||
const offset = document.offsetAt(position);
|
||||
const matches = docSrc.matchAll(macroRegex);
|
||||
|
||||
const originalUri = document.uri.toString();
|
||||
virtualDocumentContents.set(originalUri, getCSSVirtualContent(htmlLanguageService, document.getText()));
|
||||
// Lazily loop through matches, abort early if the cursor is after the match
|
||||
// let start = 0;
|
||||
// let end = 0;
|
||||
let matchBody: string | undefined = undefined;
|
||||
|
||||
const vdocUriString = `embedded-content://css/${encodeURIComponent(
|
||||
originalUri
|
||||
)}.css`;
|
||||
const vdocUri = Uri.parse(vdocUriString);
|
||||
return await commands.executeCommand<CompletionList>(
|
||||
'vscode.executeCompletionItemProvider',
|
||||
vdocUri,
|
||||
position,
|
||||
context.triggerCharacter
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const match of matches) {
|
||||
// // Check if the cursor is inbetween the previous end and the new start
|
||||
// // This means the cursor is between html! invocations and we should bail early
|
||||
// if (offset > end && offset < match.index) {
|
||||
// // Ensure the match
|
||||
// // defer to the "next?" symbol
|
||||
// return await next(document, position, context, token);
|
||||
// }
|
||||
|
||||
// Create the language client and start the client.
|
||||
client = new LanguageClient(
|
||||
'languageServerExample',
|
||||
'Language Server Example',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
// Otherwise, move the counters forward
|
||||
const start = match.index;
|
||||
const end = start + match.length;
|
||||
|
||||
// Start the client. This will also launch the server
|
||||
client.start();
|
||||
// Ensure the cursor is within the match
|
||||
// Break if so
|
||||
if (offset >= start && offset <= end) {
|
||||
matchBody = match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we looped through all the matches and the match wasn't defined, then bail
|
||||
if (matchBody === undefined) {
|
||||
return await next(document, position, context, token);
|
||||
}
|
||||
|
||||
// If we're inside the style region, then provide CSS completions with the CSS provider
|
||||
const originalUri = document.uri.toString();
|
||||
virtualDocumentContents.set(originalUri, matchBody);
|
||||
// getCSSVirtualContent(htmlLanguageService, document.getText())
|
||||
|
||||
const vdocUriString = `embedded-content://html/${encodeURIComponent(
|
||||
originalUri
|
||||
)}.html`;
|
||||
|
||||
const vdocUri = Uri.parse(vdocUriString);
|
||||
return await commands.executeCommand<CompletionList>(
|
||||
"vscode.executeCompletionItemProvider",
|
||||
vdocUri,
|
||||
position,
|
||||
context.triggerCharacter
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
client = new LanguageClient(
|
||||
"languageServerExample",
|
||||
"Language Server Example",
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
// Start the client. This will also launch the server
|
||||
client.start();
|
||||
}
|
||||
|
||||
export function deactivate(): Thenable<void> | undefined {
|
||||
if (!client) {
|
||||
return undefined;
|
||||
}
|
||||
return client.stop();
|
||||
if (!client) {
|
||||
return undefined;
|
||||
}
|
||||
return client.stop();
|
||||
}
|
||||
|
||||
const macroRegex = /html! {([\s\S]*?)}/g;
|
||||
@@ -0,0 +1,8 @@
|
||||
const macroRegex = /html! {([\s\S]*?)}/g;
|
||||
|
||||
export function isInsideHtmlMacro(
|
||||
match: RegExpMatchArray,
|
||||
cursor: number
|
||||
): boolean {
|
||||
return false;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2019",
|
||||
"lib": ["ES2019"],
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext"],
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true
|
||||
|
||||
@@ -16,26 +16,9 @@
|
||||
"node": "*"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onLanguage:html1"
|
||||
"onLanguage:rust"
|
||||
],
|
||||
"main": "./client/out/extension",
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "rsx",
|
||||
"extensions": [
|
||||
".rs"
|
||||
]
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "rsx",
|
||||
"scopeName": "text.rsx.basic",
|
||||
"path": "./syntaxes/rsx.tmLanguage.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "cd client && npm install && cd .. && npm run compile",
|
||||
"compile": "tsc -b",
|
||||
|
||||
@@ -3,9 +3,15 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { getLanguageService } from 'vscode-html-languageservice';
|
||||
import { createConnection, InitializeParams, ProposedFeatures, TextDocuments, TextDocumentSyncKind } from 'vscode-languageserver';
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import { getLanguageService } from "vscode-html-languageservice";
|
||||
import {
|
||||
createConnection,
|
||||
InitializeParams,
|
||||
ProposedFeatures,
|
||||
TextDocuments,
|
||||
TextDocumentSyncKind,
|
||||
} from "vscode-languageserver";
|
||||
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||
|
||||
// Create a connection for the server. The connection uses Node's IPC as a transport.
|
||||
// Also include all preview / proposed LSP features.
|
||||
@@ -18,30 +24,30 @@ let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
||||
const htmlLanguageService = getLanguageService();
|
||||
|
||||
connection.onInitialize((_params: InitializeParams) => {
|
||||
return {
|
||||
capabilities: {
|
||||
textDocumentSync: TextDocumentSyncKind.Full,
|
||||
// Tell the client that the server supports code completion
|
||||
completionProvider: {
|
||||
resolveProvider: false
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
capabilities: {
|
||||
textDocumentSync: TextDocumentSyncKind.Full,
|
||||
// Tell the client that the server supports code completion
|
||||
completionProvider: {
|
||||
resolveProvider: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
connection.onInitialized(() => { });
|
||||
connection.onInitialized(() => {});
|
||||
|
||||
connection.onCompletion(async (textDocumentPosition, token) => {
|
||||
const document = documents.get(textDocumentPosition.textDocument.uri);
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
const document = documents.get(textDocumentPosition.textDocument.uri);
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return htmlLanguageService.doComplete(
|
||||
document,
|
||||
textDocumentPosition.position,
|
||||
htmlLanguageService.parseHTMLDocument(document)
|
||||
);
|
||||
return htmlLanguageService.doComplete(
|
||||
document,
|
||||
textDocumentPosition.position,
|
||||
htmlLanguageService.parseHTMLDocument(document)
|
||||
);
|
||||
});
|
||||
|
||||
documents.listen(connection);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2019",
|
||||
"lib": ["ES2019"],
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext"],
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2019",
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"ES2019"
|
||||
"ESNext"
|
||||
],
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
|
||||
Reference in new issue
Block a user