forked from Mirrors/helix
Merge branch 'master'
commit
e20886dd60
@ -0,0 +1,139 @@
|
||||
name: Build
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: '00 01 * * *'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust: [stable, msrv]
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Use MSRV rust toolchain
|
||||
if: matrix.rust == 'msrv'
|
||||
run: cp .github/workflows/msrv-rust-toolchain.toml rust-toolchain.toml
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run cargo check
|
||||
run: cargo check
|
||||
|
||||
test:
|
||||
name: Test Suite
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
HELIX_LOG_LEVEL: info
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Cache test tree-sitter grammar
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: runtime/grammars
|
||||
key: ${{ runner.os }}-stable-v${{ env.CACHE_VERSION }}-tree-sitter-grammars-${{ hashFiles('languages.toml') }}
|
||||
restore-keys: ${{ runner.os }}-stable-v${{ env.CACHE_VERSION }}-tree-sitter-grammars-
|
||||
|
||||
- name: Run cargo test
|
||||
run: cargo test --workspace
|
||||
|
||||
- name: Run cargo integration-test
|
||||
run: cargo integration-test
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
lints:
|
||||
name: Lints
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
- name: Run cargo clippy
|
||||
run: cargo clippy --workspace --all-targets -- -D warnings
|
||||
|
||||
- name: Run cargo doc
|
||||
run: cargo doc --no-deps --workspace --document-private-items
|
||||
env:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
|
||||
docs:
|
||||
name: Docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Generate docs
|
||||
run: cargo xtask docgen
|
||||
|
||||
- name: Check uncommitted documentation changes
|
||||
run: |
|
||||
git diff
|
||||
git diff-files --quiet \
|
||||
|| (echo "Run 'cargo xtask docgen', commit the changes and push again" \
|
||||
&& exit 1)
|
||||
|
||||
queries:
|
||||
name: Tree-sitter queries
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Generate docs
|
||||
run: cargo xtask query-check
|
@ -0,0 +1,139 @@
|
||||
name: Build
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: '00 01 * * *'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust: [stable, msrv]
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Use MSRV rust toolchain
|
||||
if: matrix.rust == 'msrv'
|
||||
run: cp .github/workflows/msrv-rust-toolchain.toml rust-toolchain.toml
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run cargo check
|
||||
run: cargo check
|
||||
|
||||
test:
|
||||
name: Test Suite
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
HELIX_LOG_LEVEL: info
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Cache test tree-sitter grammar
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: runtime/grammars
|
||||
key: ${{ runner.os }}-stable-v${{ env.CACHE_VERSION }}-tree-sitter-grammars-${{ hashFiles('languages.toml') }}
|
||||
restore-keys: ${{ runner.os }}-stable-v${{ env.CACHE_VERSION }}-tree-sitter-grammars-
|
||||
|
||||
- name: Run cargo test
|
||||
run: cargo test --workspace
|
||||
|
||||
- name: Run cargo integration-test
|
||||
run: cargo integration-test
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
lints:
|
||||
name: Lints
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
- name: Run cargo clippy
|
||||
run: cargo clippy --workspace --all-targets -- -D warnings
|
||||
|
||||
- name: Run cargo doc
|
||||
run: cargo doc --no-deps --workspace --document-private-items
|
||||
env:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
|
||||
docs:
|
||||
name: Docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Generate docs
|
||||
run: cargo xtask docgen
|
||||
|
||||
- name: Check uncommitted documentation changes
|
||||
run: |
|
||||
git diff
|
||||
git diff-files --quiet \
|
||||
|| (echo "Run 'cargo xtask docgen', commit the changes and push again" \
|
||||
&& exit 1)
|
||||
|
||||
queries:
|
||||
name: Tree-sitter queries
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: helix-editor/rust-toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Generate docs
|
||||
run: cargo xtask query-check
|
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="CPP_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/helix.iml" filepath="$PROJECT_DIR$/.idea/helix.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,17 +0,0 @@
|
||||
use crate::{Rope, Selection};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State {
|
||||
pub doc: Rope,
|
||||
pub selection: Selection,
|
||||
}
|
||||
|
||||
impl State {
|
||||
#[must_use]
|
||||
pub fn new(doc: Rope) -> Self {
|
||||
Self {
|
||||
doc,
|
||||
selection: Selection::point(0),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,696 @@
|
||||
mod client;
|
||||
pub mod jsonrpc;
|
||||
mod transport;
|
||||
|
||||
pub use client::Client;
|
||||
pub use futures_executor::block_on;
|
||||
pub use jsonrpc::Call;
|
||||
pub use lsp::{Position, Url};
|
||||
pub use lsp_types as lsp;
|
||||
|
||||
use futures_util::stream::select_all::SelectAll;
|
||||
use helix_core::syntax::{LanguageConfiguration, LanguageServerConfiguration};
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
type LanguageId = String;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("protocol error: {0}")]
|
||||
Rpc(#[from] jsonrpc::Error),
|
||||
#[error("failed to parse: {0}")]
|
||||
Parse(#[from] serde_json::Error),
|
||||
#[error("IO Error: {0}")]
|
||||
IO(#[from] std::io::Error),
|
||||
#[error("request timed out")]
|
||||
Timeout,
|
||||
#[error("server closed the stream")]
|
||||
StreamClosed,
|
||||
#[error("LPS not defined")]
|
||||
LspNotDefined,
|
||||
#[error("Unhandled")]
|
||||
Unhandled,
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum OffsetEncoding {
|
||||
/// UTF-8 code units aka bytes
|
||||
#[serde(rename = "utf-8")]
|
||||
Utf8,
|
||||
/// UTF-16 code units
|
||||
#[serde(rename = "utf-16")]
|
||||
Utf16,
|
||||
}
|
||||
|
||||
pub mod util {
|
||||
use super::*;
|
||||
use helix_core::{diagnostic::NumberOrString, Range, Rope, Transaction};
|
||||
|
||||
/// Converts a diagnostic in the document to [`lsp::Diagnostic`].
|
||||
///
|
||||
/// Panics when [`pos_to_lsp_pos`] would for an invalid range on the diagnostic.
|
||||
pub fn diagnostic_to_lsp_diagnostic(
|
||||
doc: &Rope,
|
||||
diag: &helix_core::diagnostic::Diagnostic,
|
||||
offset_encoding: OffsetEncoding,
|
||||
) -> lsp::Diagnostic {
|
||||
use helix_core::diagnostic::Severity::*;
|
||||
|
||||
let range = Range::new(diag.range.start, diag.range.end);
|
||||
let severity = diag.severity.map(|s| match s {
|
||||
Hint => lsp::DiagnosticSeverity::HINT,
|
||||
Info => lsp::DiagnosticSeverity::INFORMATION,
|
||||
Warning => lsp::DiagnosticSeverity::WARNING,
|
||||
Error => lsp::DiagnosticSeverity::ERROR,
|
||||
});
|
||||
|
||||
let code = match diag.code.clone() {
|
||||
Some(x) => match x {
|
||||
NumberOrString::Number(x) => Some(lsp::NumberOrString::Number(x)),
|
||||
NumberOrString::String(x) => Some(lsp::NumberOrString::String(x)),
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let new_tags: Vec<_> = diag
|
||||
.tags
|
||||
.iter()
|
||||
.map(|tag| match tag {
|
||||
helix_core::diagnostic::DiagnosticTag::Unnecessary => {
|
||||
lsp::DiagnosticTag::UNNECESSARY
|
||||
}
|
||||
helix_core::diagnostic::DiagnosticTag::Deprecated => lsp::DiagnosticTag::DEPRECATED,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let tags = if !new_tags.is_empty() {
|
||||
Some(new_tags)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// TODO: add support for Diagnostic.data
|
||||
lsp::Diagnostic::new(
|
||||
range_to_lsp_range(doc, range, offset_encoding),
|
||||
severity,
|
||||
code,
|
||||
diag.source.clone(),
|
||||
diag.message.to_owned(),
|
||||
None,
|
||||
tags,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts [`lsp::Position`] to a position in the document.
|
||||
///
|
||||
/// Returns `None` if position exceeds document length or an operation overflows.
|
||||
pub fn lsp_pos_to_pos(
|
||||
doc: &Rope,
|
||||
pos: lsp::Position,
|
||||
offset_encoding: OffsetEncoding,
|
||||
) -> Option<usize> {
|
||||
let pos_line = pos.line as usize;
|
||||
if pos_line > doc.len_lines() - 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
match offset_encoding {
|
||||
OffsetEncoding::Utf8 => {
|
||||
let line = doc.line_to_char(pos_line);
|
||||
let pos = line.checked_add(pos.character as usize)?;
|
||||
if pos <= doc.len_chars() {
|
||||
Some(pos)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
OffsetEncoding::Utf16 => {
|
||||
let line = doc.line_to_char(pos_line);
|
||||
let line_start = doc.char_to_utf16_cu(line);
|
||||
let pos = line_start.checked_add(pos.character as usize)?;
|
||||
doc.try_utf16_cu_to_char(pos).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts position in the document to [`lsp::Position`].
|
||||
///
|
||||
/// Panics when `pos` is out of `doc` bounds or operation overflows.
|
||||
pub fn pos_to_lsp_pos(
|
||||
doc: &Rope,
|
||||
pos: usize,
|
||||
offset_encoding: OffsetEncoding,
|
||||
) -> lsp::Position {
|
||||
match offset_encoding {
|
||||
OffsetEncoding::Utf8 => {
|
||||
let line = doc.char_to_line(pos);
|
||||
let line_start = doc.line_to_char(line);
|
||||
let col = pos - line_start;
|
||||
|
||||
lsp::Position::new(line as u32, col as u32)
|
||||
}
|
||||
OffsetEncoding::Utf16 => {
|
||||
let line = doc.char_to_line(pos);
|
||||
let line_start = doc.char_to_utf16_cu(doc.line_to_char(line));
|
||||
let col = doc.char_to_utf16_cu(pos) - line_start;
|
||||
|
||||
lsp::Position::new(line as u32, col as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a range in the document to [`lsp::Range`].
|
||||
pub fn range_to_lsp_range(
|
||||
doc: &Rope,
|
||||
range: Range,
|
||||
offset_encoding: OffsetEncoding,
|
||||
) -> lsp::Range {
|
||||
let start = pos_to_lsp_pos(doc, range.from(), offset_encoding);
|
||||
let end = pos_to_lsp_pos(doc, range.to(), offset_encoding);
|
||||
|
||||
lsp::Range::new(start, end)
|
||||
}
|
||||
|
||||
pub fn lsp_range_to_range(
|
||||
doc: &Rope,
|
||||
range: lsp::Range,
|
||||
offset_encoding: OffsetEncoding,
|
||||
) -> Option<Range> {
|
||||
let start = lsp_pos_to_pos(doc, range.start, offset_encoding)?;
|
||||
let end = lsp_pos_to_pos(doc, range.end, offset_encoding)?;
|
||||
|
||||
Some(Range::new(start, end))
|
||||
}
|
||||
|
||||
pub fn generate_transaction_from_edits(
|
||||
doc: &Rope,
|
||||
mut edits: Vec<lsp::TextEdit>,
|
||||
offset_encoding: OffsetEncoding,
|
||||
) -> Transaction {
|
||||
// Sort edits by start range, since some LSPs (Omnisharp) send them
|
||||
// in reverse order.
|
||||
edits.sort_unstable_by_key(|edit| edit.range.start);
|
||||
|
||||
// Generate a diff if the edit is a full document replacement.
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if edits.len() == 1 {
|
||||
let is_document_replacement = edits.first().and_then(|edit| {
|
||||
let start = lsp_pos_to_pos(doc, edit.range.start, offset_encoding)?;
|
||||
let end = lsp_pos_to_pos(doc, edit.range.end, offset_encoding)?;
|
||||
Some(start..end)
|
||||
}) == Some(0..doc.len_chars());
|
||||
if is_document_replacement {
|
||||
let new_text = Rope::from(edits.pop().unwrap().new_text);
|
||||
return helix_core::diff::compare_ropes(doc, &new_text);
|
||||
}
|
||||
}
|
||||
|
||||
Transaction::change(
|
||||
doc,
|
||||
edits.into_iter().map(|edit| {
|
||||
// simplify "" into None for cleaner changesets
|
||||
let replacement = if !edit.new_text.is_empty() {
|
||||
Some(edit.new_text.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let start =
|
||||
if let Some(start) = lsp_pos_to_pos(doc, edit.range.start, offset_encoding) {
|
||||
start
|
||||
} else {
|
||||
return (0, 0, None);
|
||||
};
|
||||
let end = if let Some(end) = lsp_pos_to_pos(doc, edit.range.end, offset_encoding) {
|
||||
end
|
||||
} else {
|
||||
return (0, 0, None);
|
||||
};
|
||||
(start, end, replacement)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum MethodCall {
|
||||
WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
|
||||
ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
|
||||
WorkspaceFolders,
|
||||
WorkspaceConfiguration(lsp::ConfigurationParams),
|
||||
}
|
||||
|
||||
impl MethodCall {
|
||||
pub fn parse(method: &str, params: jsonrpc::Params) -> Result<MethodCall> {
|
||||
use lsp::request::Request;
|
||||
let request = match method {
|
||||
lsp::request::WorkDoneProgressCreate::METHOD => {
|
||||
let params: lsp::WorkDoneProgressCreateParams = params.parse()?;
|
||||
Self::WorkDoneProgressCreate(params)
|
||||
}
|
||||
lsp::request::ApplyWorkspaceEdit::METHOD => {
|
||||
let params: lsp::ApplyWorkspaceEditParams = params.parse()?;
|
||||
Self::ApplyWorkspaceEdit(params)
|
||||
}
|
||||
lsp::request::WorkspaceFoldersRequest::METHOD => Self::WorkspaceFolders,
|
||||
lsp::request::WorkspaceConfiguration::METHOD => {
|
||||
let params: lsp::ConfigurationParams = params.parse()?;
|
||||
Self::WorkspaceConfiguration(params)
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::Unhandled);
|
||||
}
|
||||
};
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Notification {
|
||||
// we inject this notification to signal the LSP is ready
|
||||
Initialized,
|
||||
// and this notification to signal that the LSP exited
|
||||
Exit,
|
||||
PublishDiagnostics(lsp::PublishDiagnosticsParams),
|
||||
ShowMessage(lsp::ShowMessageParams),
|
||||
LogMessage(lsp::LogMessageParams),
|
||||
ProgressMessage(lsp::ProgressParams),
|
||||
}
|
||||
|
||||
impl Notification {
|
||||
pub fn parse(method: &str, params: jsonrpc::Params) -> Result<Notification> {
|
||||
use lsp::notification::Notification as _;
|
||||
|
||||
let notification = match method {
|
||||
lsp::notification::Initialized::METHOD => Self::Initialized,
|
||||
lsp::notification::Exit::METHOD => Self::Exit,
|
||||
lsp::notification::PublishDiagnostics::METHOD => {
|
||||
let params: lsp::PublishDiagnosticsParams = params.parse()?;
|
||||
Self::PublishDiagnostics(params)
|
||||
}
|
||||
|
||||
lsp::notification::ShowMessage::METHOD => {
|
||||
let params: lsp::ShowMessageParams = params.parse()?;
|
||||
Self::ShowMessage(params)
|
||||
}
|
||||
lsp::notification::LogMessage::METHOD => {
|
||||
let params: lsp::LogMessageParams = params.parse()?;
|
||||
Self::LogMessage(params)
|
||||
}
|
||||
lsp::notification::Progress::METHOD => {
|
||||
let params: lsp::ProgressParams = params.parse()?;
|
||||
Self::ProgressMessage(params)
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::Unhandled);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(notification)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Registry {
|
||||
inner: HashMap<LanguageId, (usize, Arc<Client>)>,
|
||||
|
||||
counter: AtomicUsize,
|
||||
pub incoming: SelectAll<UnboundedReceiverStream<(usize, Call)>>,
|
||||
}
|
||||
|
||||
impl Default for Registry {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: HashMap::new(),
|
||||
counter: AtomicUsize::new(0),
|
||||
incoming: SelectAll::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_by_id(&self, id: usize) -> Option<&Client> {
|
||||
self.inner
|
||||
.values()
|
||||
.find(|(client_id, _)| client_id == &id)
|
||||
.map(|(_, client)| client.as_ref())
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
||||||| 4ec2a21c
|
||||
pub fn restart(
|
||||
&mut self,
|
||||
language_config: &LanguageConfiguration,
|
||||
doc_path: Option<&std::path::PathBuf>,
|
||||
) -> Result<Option<Arc<Client>>> {
|
||||
let config = match &language_config.language_server {
|
||||
Some(config) => config,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let scope = language_config.scope.clone();
|
||||
|
||||
match self.inner.entry(scope) {
|
||||
Entry::Vacant(_) => Ok(None),
|
||||
Entry::Occupied(mut entry) => {
|
||||
// initialize a new client
|
||||
let id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let NewClientResult(client, incoming) =
|
||||
start_client(id, language_config, config, doc_path)?;
|
||||
self.incoming.push(UnboundedReceiverStream::new(incoming));
|
||||
|
||||
let (_, old_client) = entry.insert((id, client.clone()));
|
||||
|
||||
tokio::spawn(async move {
|
||||
let _ = old_client.force_shutdown().await;
|
||||
});
|
||||
|
||||
Ok(Some(client))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=======
|
||||
pub fn remove_by_id(&mut self, id: usize) {
|
||||
self.inner.retain(|_, (client_id, _)| client_id != &id)
|
||||
}
|
||||
|
||||
pub fn restart(
|
||||
&mut self,
|
||||
language_config: &LanguageConfiguration,
|
||||
doc_path: Option<&std::path::PathBuf>,
|
||||
) -> Result<Option<Arc<Client>>> {
|
||||
let config = match &language_config.language_server {
|
||||
Some(config) => config,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let scope = language_config.scope.clone();
|
||||
|
||||
match self.inner.entry(scope) {
|
||||
Entry::Vacant(_) => Ok(None),
|
||||
Entry::Occupied(mut entry) => {
|
||||
// initialize a new client
|
||||
let id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let NewClientResult(client, incoming) =
|
||||
start_client(id, language_config, config, doc_path)?;
|
||||
self.incoming.push(UnboundedReceiverStream::new(incoming));
|
||||
|
||||
let (_, old_client) = entry.insert((id, client.clone()));
|
||||
|
||||
tokio::spawn(async move {
|
||||
let _ = old_client.force_shutdown().await;
|
||||
});
|
||||
|
||||
Ok(Some(client))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
>>>>>>> master
|
||||
pub fn get(
|
||||
&mut self,
|
||||
language_config: &LanguageConfiguration,
|
||||
doc_path: Option<&std::path::PathBuf>,
|
||||
) -> Result<Option<Arc<Client>>> {
|
||||
let config = match &language_config.language_server {
|
||||
Some(config) => config,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
match self.inner.entry(language_config.scope.clone()) {
|
||||
Entry::Occupied(entry) => Ok(Some(entry.get().1.clone())),
|
||||
Entry::Vacant(entry) => {
|
||||
// initialize a new client
|
||||
let id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let NewClientResult(client, incoming) =
|
||||
start_client(id, language_config, config, doc_path)?;
|
||||
self.incoming.push(UnboundedReceiverStream::new(incoming));
|
||||
|
||||
entry.insert((id, client.clone()));
|
||||
Ok(Some(client))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restart(
|
||||
&mut self,
|
||||
language_config: &LanguageConfiguration,
|
||||
path: Option<&PathBuf>,
|
||||
) -> Result<Arc<Client>> {
|
||||
let config = language_config
|
||||
.language_server
|
||||
.as_ref()
|
||||
.ok_or(Error::LspNotDefined)?;
|
||||
let id = self
|
||||
.inner
|
||||
.get(&language_config.scope)
|
||||
.ok_or(Error::LspNotDefined)?
|
||||
.0;
|
||||
let new_client = self.initialize_client(language_config, config, id, path)?;
|
||||
let (_, client) = self
|
||||
.inner
|
||||
.get_mut(&language_config.scope)
|
||||
.ok_or(Error::LspNotDefined)?;
|
||||
*client = new_client;
|
||||
|
||||
Ok(client.clone())
|
||||
}
|
||||
|
||||
fn initialize_client(
|
||||
&mut self,
|
||||
language_config: &LanguageConfiguration,
|
||||
config: &helix_core::syntax::LanguageServerConfiguration,
|
||||
id: usize,
|
||||
path: Option<&PathBuf>,
|
||||
) -> Result<Arc<Client>> {
|
||||
let (client, incoming, initialize_notify) = Client::start(
|
||||
&config.command,
|
||||
&config.args,
|
||||
language_config.config.clone(),
|
||||
&language_config.roots,
|
||||
id,
|
||||
config.timeout,
|
||||
path,
|
||||
)?;
|
||||
self.incoming.push(UnboundedReceiverStream::new(incoming));
|
||||
let client = Arc::new(client);
|
||||
|
||||
// Initialize the client asynchronously
|
||||
let _client = client.clone();
|
||||
tokio::spawn(async move {
|
||||
use futures_util::TryFutureExt;
|
||||
let value = _client
|
||||
.capabilities
|
||||
.get_or_try_init(|| {
|
||||
_client
|
||||
.initialize()
|
||||
.map_ok(|response| response.capabilities)
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Err(e) = value {
|
||||
log::error!("failed to initialize language server: {}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// next up, notify<initialized>
|
||||
_client
|
||||
.notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
initialize_notify.notify_one();
|
||||
});
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub fn iter_clients(&self) -> impl Iterator<Item = &Arc<Client>> {
|
||||
self.inner.values().map(|(_, client)| client)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ProgressStatus {
|
||||
Created,
|
||||
Started(lsp::WorkDoneProgress),
|
||||
}
|
||||
|
||||
impl ProgressStatus {
|
||||
pub fn progress(&self) -> Option<&lsp::WorkDoneProgress> {
|
||||
match &self {
|
||||
ProgressStatus::Created => None,
|
||||
ProgressStatus::Started(progress) => Some(progress),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
/// Acts as a container for progress reported by language servers. Each server
|
||||
/// has a unique id assigned at creation through [`Registry`]. This id is then used
|
||||
/// to store the progress in this map.
|
||||
pub struct LspProgressMap(HashMap<usize, HashMap<lsp::ProgressToken, ProgressStatus>>);
|
||||
|
||||
impl LspProgressMap {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Returns a map of all tokens corresponding to the language server with `id`.
|
||||
pub fn progress_map(&self, id: usize) -> Option<&HashMap<lsp::ProgressToken, ProgressStatus>> {
|
||||
self.0.get(&id)
|
||||
}
|
||||
|
||||
pub fn is_progressing(&self, id: usize) -> bool {
|
||||
self.0.get(&id).map(|it| !it.is_empty()).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns last progress status for a given server with `id` and `token`.
|
||||
pub fn progress(&self, id: usize, token: &lsp::ProgressToken) -> Option<&ProgressStatus> {
|
||||
self.0.get(&id).and_then(|values| values.get(token))
|
||||
}
|
||||
|
||||
/// Checks if progress `token` for server with `id` is created.
|
||||
pub fn is_created(&mut self, id: usize, token: &lsp::ProgressToken) -> bool {
|
||||
self.0
|
||||
.get(&id)
|
||||
.map(|values| values.get(token).is_some())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn create(&mut self, id: usize, token: lsp::ProgressToken) {
|
||||
self.0
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.insert(token, ProgressStatus::Created);
|
||||
}
|
||||
|
||||
/// Ends the progress by removing the `token` from server with `id`, if removed returns the value.
|
||||
pub fn end_progress(
|
||||
&mut self,
|
||||
id: usize,
|
||||
token: &lsp::ProgressToken,
|
||||
) -> Option<ProgressStatus> {
|
||||
self.0.get_mut(&id).and_then(|vals| vals.remove(token))
|
||||
}
|
||||
|
||||
/// Updates the progress of `token` for server with `id` to `status`, returns the value replaced or `None`.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
id: usize,
|
||||
token: lsp::ProgressToken,
|
||||
status: lsp::WorkDoneProgress,
|
||||
) -> Option<ProgressStatus> {
|
||||
self.0
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.insert(token, ProgressStatus::Started(status))
|
||||
}
|
||||
}
|
||||
|
||||
struct NewClientResult(Arc<Client>, UnboundedReceiver<(usize, Call)>);
|
||||
|
||||
/// start_client takes both a LanguageConfiguration and a LanguageServerConfiguration to ensure that
|
||||
/// it is only called when it makes sense.
|
||||
fn start_client(
|
||||
id: usize,
|
||||
config: &LanguageConfiguration,
|
||||
ls_config: &LanguageServerConfiguration,
|
||||
doc_path: Option<&std::path::PathBuf>,
|
||||
) -> Result<NewClientResult> {
|
||||
let (client, incoming, initialize_notify) = Client::start(
|
||||
&ls_config.command,
|
||||
&ls_config.args,
|
||||
config.config.clone(),
|
||||
&config.roots,
|
||||
id,
|
||||
ls_config.timeout,
|
||||
doc_path,
|
||||
)?;
|
||||
|
||||
let client = Arc::new(client);
|
||||
|
||||
// Initialize the client asynchronously
|
||||
let _client = client.clone();
|
||||
tokio::spawn(async move {
|
||||
use futures_util::TryFutureExt;
|
||||
let value = _client
|
||||
.capabilities
|
||||
.get_or_try_init(|| {
|
||||
_client
|
||||
.initialize()
|
||||
.map_ok(|response| response.capabilities)
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Err(e) = value {
|
||||
log::error!("failed to initialize language server: {}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// next up, notify<initialized>
|
||||
_client
|
||||
.notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
initialize_notify.notify_one();
|
||||
});
|
||||
|
||||
Ok(NewClientResult(client, incoming))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{lsp, util::*, OffsetEncoding};
|
||||
use helix_core::Rope;
|
||||
|
||||
#[test]
|
||||
fn converts_lsp_pos_to_pos() {
|
||||
macro_rules! test_case {
|
||||
($doc:expr, ($x:expr, $y:expr) => $want:expr) => {
|
||||
let doc = Rope::from($doc);
|
||||
let pos = lsp::Position::new($x, $y);
|
||||
assert_eq!($want, lsp_pos_to_pos(&doc, pos, OffsetEncoding::Utf16));
|
||||
assert_eq!($want, lsp_pos_to_pos(&doc, pos, OffsetEncoding::Utf8))
|
||||
};
|
||||
}
|
||||
|
||||
test_case!("", (0, 0) => Some(0));
|
||||
test_case!("", (0, 1) => None);
|
||||
test_case!("", (1, 0) => None);
|
||||
test_case!("\n\n", (0, 0) => Some(0));
|
||||
test_case!("\n\n", (1, 0) => Some(1));
|
||||
test_case!("\n\n", (1, 1) => Some(2));
|
||||
test_case!("\n\n", (2, 0) => Some(2));
|
||||
test_case!("\n\n", (3, 0) => None);
|
||||
test_case!("test\n\n\n\ncase", (4, 3) => Some(11));
|
||||
test_case!("test\n\n\n\ncase", (4, 4) => Some(12));
|
||||
test_case!("test\n\n\n\ncase", (4, 5) => None);
|
||||
test_case!("", (u32::MAX, u32::MAX) => None);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,73 @@
|
||||
; Keywords
|
||||
|
||||
[
|
||||
"module"
|
||||
"var"
|
||||
"param"
|
||||
"import"
|
||||
"resource"
|
||||
"existing"
|
||||
"if"
|
||||
"targetScope"
|
||||
"output"
|
||||
] @keyword
|
||||
|
||||
; Functions
|
||||
|
||||
(decorator) @function.builtin
|
||||
|
||||
(functionCall) @function
|
||||
|
||||
(functionCall
|
||||
(functionArgument
|
||||
(variableAccess) @variable))
|
||||
|
||||
; Literals/Types
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
(resourceDeclaration
|
||||
(string
|
||||
(stringLiteral) @string.special))
|
||||
|
||||
(moduleDeclaration
|
||||
(string
|
||||
(stringLiteral) @string.special))
|
||||
|
||||
[
|
||||
(string)
|
||||
(stringLiteral)
|
||||
] @string
|
||||
|
||||
(nullLiteral) @keyword
|
||||
(booleanLiteral) @constant.builtin.boolean
|
||||
(integerLiteral) @constant.numeric.integer
|
||||
(comment) @comment
|
||||
|
||||
(string
|
||||
(variableAccess
|
||||
(identifier) @variable))
|
||||
|
||||
(type) @type
|
||||
|
||||
; Variables
|
||||
|
||||
(localVariable) @variable
|
||||
|
||||
; Statements
|
||||
|
||||
(object
|
||||
(objectProperty
|
||||
(identifier) @identifier))
|
||||
|
||||
(propertyAccess
|
||||
(identifier) @identifier)
|
||||
|
||||
(ifCondition) @keyword.control.conditional
|
@ -0,0 +1,13 @@
|
||||
[
|
||||
(after_block)
|
||||
(anonymous_function)
|
||||
(catch_block)
|
||||
(do_block)
|
||||
(else_block)
|
||||
(rescue_block)
|
||||
(stab_clause)
|
||||
] @indent
|
||||
|
||||
[
|
||||
"end"
|
||||
] @outdent
|
@ -1,2 +1,7 @@
|
||||
((comment_content) @injection.content
|
||||
(#set! injection.language "edoc"))
|
||||
((line_comment (comment_content) @injection.content)
|
||||
(#set! injection.language "edoc")
|
||||
(#set! injection.include-children)
|
||||
(#set! injection.combined))
|
||||
|
||||
((comment (comment_content) @injection.content)
|
||||
(#set! injection.language "comment"))
|
||||
|
@ -0,0 +1,30 @@
|
||||
; Specs and Callbacks
|
||||
(attribute
|
||||
(stab_clause
|
||||
pattern: (arguments (variable) @local.definition)
|
||||
; If a spec uses a variable as the return type (and later a `when` clause to type it):
|
||||
body: (variable)? @local.definition)) @local.scope
|
||||
|
||||
; parametric `-type`s
|
||||
((attribute
|
||||
name: (atom) @_type
|
||||
(arguments
|
||||
(binary_operator
|
||||
left: (call (arguments (variable) @local.definition))
|
||||
operator: "::") @local.scope))
|
||||
(#match? @_type "(type|opaque)"))
|
||||
|
||||
; macros
|
||||
((attribute
|
||||
name: (atom) @_define
|
||||
(arguments
|
||||
(call (arguments (variable) @local.definition)))) @local.scope
|
||||
(#eq? @_define "define"))
|
||||
|
||||
; `fun`s
|
||||
(anonymous_function (stab_clause pattern: (arguments (variable) @local.definition))) @local.scope
|
||||
|
||||
; Ordinary functions
|
||||
(function_clause pattern: (arguments (variable) @local.definition)) @local.scope
|
||||
|
||||
(variable) @local.reference
|
@ -1,2 +1,2 @@
|
||||
((comment) @injection.content
|
||||
([(line_comment) (block_comment)] @injection.content
|
||||
(#set! injection.language "comment"))
|
||||
|
@ -0,0 +1,35 @@
|
||||
(method_declaration
|
||||
body: (_) @function.inside) @function.around
|
||||
|
||||
(interface_declaration
|
||||
body: (_) @class.inside) @class.around
|
||||
|
||||
(class_declaration
|
||||
body: (_) @class.inside) @class.around
|
||||
|
||||
(record_declaration
|
||||
body: (_) @class.inside) @class.around
|
||||
|
||||
(enum_declaration
|
||||
body: (_) @class.inside) @class.around
|
||||
|
||||
(formal_parameters
|
||||
((_) @parameter.inside . ","? @parameter.around) @parameter.around)
|
||||
|
||||
(type_parameters
|
||||
((_) @parameter.inside . ","? @parameter.around) @parameter.around)
|
||||
|
||||
(type_arguments
|
||||
((_) @parameter.inside . ","? @parameter.around) @parameter.around)
|
||||
|
||||
(argument_list
|
||||
((_) @parameter.inside . ","? @parameter.around) @parameter.around)
|
||||
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
] @comment.inside
|
||||
|
||||
(line_comment)+ @comment.around
|
||||
|
||||
(block_comment) @comment.around
|
@ -1,62 +1,62 @@
|
||||
(string_literal) @string
|
||||
(comment) @comment
|
||||
|
||||
(boolean_literal) @constant.builtin.boolean
|
||||
(integer_literal) @constant.numeric.integer
|
||||
|
||||
(comment) @comment.line
|
||||
(function_id) @function
|
||||
(keyword_arg_key) @variable.other.member
|
||||
(id_expression) @variable
|
||||
; these are listed first, because they override keyword queries
|
||||
(function_expression (identifier) @function)
|
||||
|
||||
[
|
||||
"if"
|
||||
"elif"
|
||||
"else"
|
||||
"endif"
|
||||
] @keyword.control.conditional
|
||||
(assignment_operator)
|
||||
(additive_operator)
|
||||
(multiplicative_operator)
|
||||
(equality_operator)
|
||||
">="
|
||||
"<="
|
||||
"<"
|
||||
">"
|
||||
"+"
|
||||
"-"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"foreach"
|
||||
"endforeach"
|
||||
] @keyword.control.repeat
|
||||
(and)
|
||||
(or)
|
||||
(not)
|
||||
(in)
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"break"
|
||||
"continue"
|
||||
] @keyword.control
|
||||
"(" ")" "[" "]" "{" "}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"not"
|
||||
"in"
|
||||
"and"
|
||||
"or"
|
||||
] @keyword.operator
|
||||
(if)
|
||||
(elif)
|
||||
(else)
|
||||
(endif)
|
||||
] @keyword.control.conditional
|
||||
|
||||
[
|
||||
"!"
|
||||
"+"
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"%"
|
||||
"=="
|
||||
"!="
|
||||
">"
|
||||
"<"
|
||||
">="
|
||||
"<="
|
||||
] @operator
|
||||
(foreach)
|
||||
(endforeach)
|
||||
(break)
|
||||
(continue)
|
||||
] @keyword.control.repeat
|
||||
|
||||
(boolean_literal) @constant.builtin.boolean
|
||||
(int_literal) @constant.numeric.integer
|
||||
|
||||
(keyword_argument keyword: (identifier) @variable.parameter)
|
||||
(escape_sequence) @constant.character.escape
|
||||
(bad_escape) @warning
|
||||
|
||||
[
|
||||
":"
|
||||
","
|
||||
"."
|
||||
","
|
||||
":"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
(string_literal)
|
||||
(fstring_literal)
|
||||
] @string
|
||||
|
||||
(identifier) @variable
|
||||
|
@ -0,0 +1,90 @@
|
||||
(comment) @comment
|
||||
|
||||
(ui_import
|
||||
source: _ @namespace
|
||||
version: _? @constant
|
||||
alias: _? @namespace)
|
||||
|
||||
(ui_pragma
|
||||
name: (identifier) @attribute
|
||||
value: (identifier)? @constant)
|
||||
|
||||
(ui_annotation
|
||||
"@" @punctuation
|
||||
type_name: _ @type)
|
||||
|
||||
;;; Declarations
|
||||
|
||||
(enum_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(enum_assignment
|
||||
name: (identifier) @constant
|
||||
value: _ @constant)
|
||||
|
||||
(enum_body
|
||||
name: (identifier) @constant)
|
||||
|
||||
(ui_inline_component
|
||||
name: (identifier) @type)
|
||||
|
||||
(ui_object_definition
|
||||
type_name: _ @type)
|
||||
|
||||
(ui_object_definition_binding
|
||||
type_name: _ @type
|
||||
name: _ @variable.other.member)
|
||||
|
||||
(ui_property
|
||||
type: _ @type
|
||||
name: (identifier) @variable.other.member)
|
||||
|
||||
(ui_signal
|
||||
name: (identifier) @function)
|
||||
|
||||
(ui_signal_parameter
|
||||
name: (identifier) @variable.parameter
|
||||
type: _ @type)
|
||||
|
||||
(ui_signal_parameter
|
||||
type: _ @type
|
||||
name: (identifier) @variable.parameter);;; Properties and bindings
|
||||
|
||||
;;; Bindings
|
||||
|
||||
(ui_binding
|
||||
name: _ @variable.other.member)
|
||||
|
||||
;;; Other
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
(ui_list_property_type [
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
[
|
||||
","
|
||||
"."
|
||||
":"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"as"
|
||||
"component"
|
||||
"default"
|
||||
"enum"
|
||||
"import"
|
||||
"on"
|
||||
"pragma"
|
||||
"property"
|
||||
"readonly"
|
||||
"required"
|
||||
"signal"
|
||||
] @keyword
|
@ -0,0 +1,6 @@
|
||||
[
|
||||
(enum_body)
|
||||
(ui_object_initializer)
|
||||
] @indent
|
||||
|
||||
"}" @outdent
|
@ -0,0 +1,16 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment"))
|
||||
|
||||
([
|
||||
(empty_statement)
|
||||
(expression_statement)
|
||||
(function_declaration)
|
||||
(generator_function_declaration)
|
||||
(statement_block)
|
||||
(switch_statement)
|
||||
(try_statement)
|
||||
(variable_declaration)
|
||||
(with_statement)
|
||||
] @injection.content
|
||||
(#set! injection.include-children)
|
||||
(#set! injection.language "javascript"))
|
@ -0,0 +1,138 @@
|
||||
# Author : Rohit K Viswanath <kvrohit@gmail.com>
|
||||
# Theme: Mellow
|
||||
|
||||
"attribute" = { fg = "blue", modifiers = ["italic"] }
|
||||
|
||||
"keyword" = "blue"
|
||||
"keyword.control.conditional" = { fg = "blue", modifiers = ["italic"] }
|
||||
"keyword.directive" = "magenta" # -- preprocessor comments (#if in C)
|
||||
|
||||
"namespace" = { fg = "cyan", modifiers = ["italic"] }
|
||||
|
||||
"punctuation" = "gray06"
|
||||
"punctuation.delimiter" = "gray06"
|
||||
|
||||
"operator" = "yellow"
|
||||
"special" = "yellow"
|
||||
|
||||
"variable" = "fg"
|
||||
"variable.builtin" = "bright_blue"
|
||||
"variable.parameter" = "cyan"
|
||||
"variable.other.member" = "white"
|
||||
|
||||
"type" = "bright_blue"
|
||||
"type.builtin" = "magenta"
|
||||
"type.enum.variant" = "magenta"
|
||||
|
||||
"constructor" = "yellow"
|
||||
|
||||
"function" = "white"
|
||||
"function.macro" = "bright_cyan"
|
||||
"function.builtin" = "bright_blue"
|
||||
|
||||
"tag" = "cyan"
|
||||
"comment" = { fg = "gray05", modifiers = ["italic"] }
|
||||
|
||||
"string" = "green"
|
||||
"string.regexp" = "green"
|
||||
"string.special" = "yellow"
|
||||
|
||||
"constant" = "cyan"
|
||||
"constant.builtin" = "yellow"
|
||||
"constant.numeric" = "magenta"
|
||||
"constant.character.escape" = "cyan"
|
||||
|
||||
# used for lifetimes
|
||||
"label" = "yellow"
|
||||
|
||||
"markup.heading.marker" = { fg = "gray06" }
|
||||
"markup.heading" = { fg = "bright_blue", modifiers = ["bold"] }
|
||||
"markup.list" = "gray06"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "green", modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "blue", modifiers = ["italic"] }
|
||||
"markup.raw" = "yellow"
|
||||
|
||||
"diff.plus" = "bright_green"
|
||||
"diff.minus" = "bright_red"
|
||||
"diff.delta" = "bright_blue"
|
||||
|
||||
"ui.background" = { bg = "bg" }
|
||||
"ui.background.separator" = { fg = "fg" }
|
||||
|
||||
"ui.linenr" = { fg = "gray04" }
|
||||
"ui.linenr.selected" = { fg = "fg" }
|
||||
|
||||
"ui.statusline" = { fg = "fg", bg = "gray01" }
|
||||
"ui.statusline.inactive" = { fg = "fg", bg = "gray01", modifiers = ["dim"] }
|
||||
"ui.statusline.normal" = { fg = "bg", bg = "cyan", modifiers = ["bold"] }
|
||||
"ui.statusline.insert" = { fg = "bg", bg = "blue", modifiers = ["bold"] }
|
||||
"ui.statusline.select" = { fg = "bg", bg = "magenta", modifiers = ["bold"] }
|
||||
|
||||
"ui.popup" = { bg = "gray01" }
|
||||
"ui.window" = { fg = "gray02" }
|
||||
"ui.help" = { bg = "gray01", fg = "white" }
|
||||
|
||||
"ui.text" = { fg = "fg" }
|
||||
"ui.text.focus" = { fg = "fg" }
|
||||
|
||||
"ui.virtual" = { fg = "gray02" }
|
||||
"ui.virtual.indent-guide" = { fg = "gray02" }
|
||||
|
||||
"ui.selection" = { bg = "gray03" }
|
||||
"ui.selection.primary" = { bg = "gray03" }
|
||||
|
||||
"ui.cursor" = { bg = "gray04" }
|
||||
"ui.cursor.match" = { fg = "yellow", modifiers = ["bold", "underlined"] }
|
||||
"ui.cursorline.primary" = { bg = "gray01" }
|
||||
|
||||
"ui.highlight" = { bg = "gray02" }
|
||||
|
||||
"ui.menu" = { fg = "white", bg = "gray01" }
|
||||
"ui.menu.selected" = { fg = "bright_white", bg = "gray03" }
|
||||
"ui.menu.scroll" = { fg = "gray04", bg = "gray01" }
|
||||
|
||||
diagnostic = { modifiers = ["underlined"] }
|
||||
|
||||
warning = "bright_yellow"
|
||||
error = "bright_red"
|
||||
info = "bright_blue"
|
||||
hint = "bright_cyan"
|
||||
|
||||
[palette]
|
||||
bg = "#161617"
|
||||
fg = "#c9c7cd"
|
||||
bg_dark = "#131314"
|
||||
|
||||
black = "#27272a"
|
||||
bright_black = "#353539"
|
||||
|
||||
red = "#f5a191"
|
||||
bright_red = "#ffae9f"
|
||||
|
||||
green = "#90b99f"
|
||||
bright_green = "#9dc6ac"
|
||||
|
||||
yellow = "#e6b99d"
|
||||
bright_yellow = "#f0c5a9"
|
||||
|
||||
blue = "#aca1cf"
|
||||
bright_blue = "#b9aeda"
|
||||
|
||||
magenta = "#e29eca"
|
||||
bright_magenta = "#ecaad6"
|
||||
|
||||
cyan = "#ea83a5"
|
||||
bright_cyan = "#f591b2"
|
||||
|
||||
white = "#c1c0d4"
|
||||
bright_white = "#cac9dd"
|
||||
|
||||
gray01 = "#1b1b1d"
|
||||
gray02 = "#2a2a2d"
|
||||
gray03 = "#3e3e43"
|
||||
gray04 = "#57575f"
|
||||
gray05 = "#757581"
|
||||
gray06 = "#9998a8"
|
||||
gray07 = "#c1c0d4"
|
@ -0,0 +1,182 @@
|
||||
# Author: github.com/jhscheer
|
||||
#
|
||||
# This theme is an adaptation of
|
||||
# github.com/EdenEast/nightfox.nvim
|
||||
|
||||
|
||||
# INTERFACE
|
||||
# These scopes are used for theming the editor interface.
|
||||
|
||||
"ui.background" = { bg = "bg1" } # Default background color.
|
||||
"ui.window" = { fg = "bg0" } # Window border between splits.
|
||||
"ui.gutter" = { fg = "fg3" } # Left gutter for diagnostics and breakpoints.
|
||||
|
||||
"ui.text" = { fg = "fg1" } # Default text color.
|
||||
"ui.text.focus" = { bg = "sel1", fg = "fg1" } # Selection highlight in buffer-picker or file-picker.
|
||||
"ui.text.info" = { fg = "fg2", bg = "sel0" } # Info popup contents (space mode menu).
|
||||
|
||||
"ui.cursor" = { bg = "fg3", fg = "bg1" } # Fallback cursor colour, non-primary cursors when there are multiple (shift-c).
|
||||
"ui.cursor.primary" = { bg = "fg1", fg = "bg1" } # The primary cursor when there are multiple (shift-c).
|
||||
"ui.cursor.match" = { fg = "yellow", modifiers = ["bold"] } # The matching parentheses of that under the cursor.
|
||||
|
||||
"ui.selection" = { bg = "bg3" } # All currently selected text.
|
||||
"ui.selection.primary" = { bg = "bg4" } # The primary selection when there are multiple.
|
||||
"ui.cursorline.primary" = { bg = "bg3" } # The line of the primary cursor (if cursorline is enabled)
|
||||
# "ui.cursorline.secondary" = { } # The lines of any other cursors (if cursorline is enabled)
|
||||
# "ui.cursorcolumn.primary" = { } # The column of the primary cursor (if cursorcolumn is enabled)
|
||||
# "ui.cursorcolumn.secondary" = { } # The columns of any other cursors (if cursorcolumn is enabled)
|
||||
|
||||
"ui.linenr" = { fg = "fg3" } # Line numbers.
|
||||
"ui.linenr.selected" = { fg = "yellow", modifiers = ["bold"] } # Current line number.
|
||||
|
||||
# "ui.virtual" = { } # Namespace for additions to the editing area.
|
||||
"ui.virtual.ruler" = { bg = "bg3" } # Vertical rulers (colored columns in editing area).
|
||||
"ui.virtual.whitespace" = { fg = "bg3" } # Whitespace markers in editing area.
|
||||
"ui.virtual.indent-guide" = { fg = "black" } # Vertical indent width guides
|
||||
|
||||
"ui.statusline" = { fg = "fg2", bg = "bg0" } # Status line.
|
||||
"ui.statusline.inactive" = { fg = "fg3", bg = "bg0" } # Status line in unfocused windows.
|
||||
"ui.statusline.normal" = { bg = "blue", fg = "bg0", modifiers = ["bold"] } # Statusline mode during normal mode (only if editor.color-modes is enabled)
|
||||
"ui.statusline.insert" = { bg = "green", fg = "bg0", modifiers = ["bold"] } # Statusline mode during insert mode (only if editor.color-modes is enabled)
|
||||
"ui.statusline.select" = { bg = "magenta", fg = "bg0", modifiers = ["bold"] } # Statusline mode during select mode (only if editor.color-modes is enabled)
|
||||
|
||||
"ui.help" = { bg = "sel0", fg = "fg1" } # Description box for commands.
|
||||
|
||||
"ui.menu" = { bg = "sel0", fg = "fg1" } # Code and command completion menus.
|
||||
"ui.menu.selected" = { bg = "fg3" } # Selected autocomplete item.
|
||||
"ui.menu.scroll" = { fg = "fg3" } # fg sets thumb color, bg sets track color of scrollbar.
|
||||
|
||||
"ui.popup" = { bg = "bg0", fg = "fg1" } # Documentation popups (space-k).
|
||||
"ui.popup.info" = { bg = "sel0", fg = "fg1" } # Info popups box (space mode menu).
|
||||
|
||||
"markup.raw" = { fg = "magenta" } # Code block in Markdown.
|
||||
"markup.raw.inline" = { fg = "orange" } # `Inline code block` in Markdown.
|
||||
"markup.heading" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.list" = { fg = "magenta", modifiers = ["bold"] }
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "pink" }
|
||||
"markup.link" = { fg = "yellow-bright", modifiers = ["bold"] }
|
||||
"markup.quote" = { fg = "blue" }
|
||||
|
||||
|
||||
# DIAGNOSTICS
|
||||
"warning" = { fg ="yellow", bg = "bg1" } # Diagnostics warning (gutter)
|
||||
"error" = { fg = "red", bg = "bg1" } # Diagnostics error (gutter)
|
||||
"info" = { fg = "blue", bg = "bg1" } # Diagnostics info (gutter)
|
||||
"hint" = { fg = "green", bg = "bg1" } # Diagnostics hint (gutter)
|
||||
"diagnostic" = { modifiers = ["underlined"] } # Diagnostics fallback style (editing area)
|
||||
"diagnostic.error" = { fg = "red" } # Diagnostics error (editing area)
|
||||
"diagnostic.warning" = { fg = "yellow" } # Diagnostics warning (editing area)
|
||||
"diagnostic.info" = { fg = "blue" } # Diagnostics info (editing area)
|
||||
"diagnostic.hint" = { fg = "green" } # Diagnostics hint (editing area)
|
||||
|
||||
|
||||
# SYNTAX HIGHLIGHTING
|
||||
# These keys match tree-sitter scopes.
|
||||
|
||||
"special" = { fg = "fg2" } # Special symbols e.g `?` in Rust, `...` in Hare.
|
||||
"attribute" = { fg = "yellow" } # Class attributes, html tag attributes.
|
||||
|
||||
"type" = { fg = "yellow" } # Variable type, like integer or string, including program defined classes, structs etc..
|
||||
"type.builtin" = { fg = "cyan-bright" } # Primitive types of the language (string, int, float).
|
||||
"type.enum.variant" = { fg = "orange-bright" }
|
||||
|
||||
"constructor" = { fg = "magenta" } # Constructor method for a class or struct.
|
||||
|
||||
"constant" = { fg = "orange-bright" } # Constant value
|
||||
"constant.builtin" = { fg = "orange-bright" } # Special constants like `true`, `false`, `none`, etc.
|
||||
"constant.builtin.boolean" = { fg = "orange" } # True or False.
|
||||
"constant.character" = { fg = "green" } # Constant of character type.
|
||||
"constant.character.escape" = { fg = "yellow-bright", modifiers = ["bold"] } # escape codes like \n.
|
||||
"constant.numeric" = { fg = "orange" } # constant integer or float value.
|
||||
|
||||
"string" = { fg = "green" } # String literal.
|
||||
"string.regexp" = { fg = "yellow-bright" } # Regular expression literal.
|
||||
"string.special" = { fg = "yellow-bright", modifiers = ["bold"] } # Strings containing a path, URL, etc.
|
||||
"string.special.url" = { fg = "cyan", modifiers = ["bold"] } # String containing a web URL.
|
||||
|
||||
"comment" = { fg = "comment" } # This is a comment.
|
||||
"comment.block.documentation" = { fg = "comment", modifiers = ["bold"] } # Doc comments, e.g '///' in rust.
|
||||
|
||||
"variable" = { fg = "white" } # Variable names.
|
||||
"variable.builtin" = { fg = "red" } # Language reserved variables: `this`, `self`, `super`, etc.
|
||||
"variable.parameter" = { fg = "cyan-bright" } # Function parameters.
|
||||
"variable.other.member" = { fg = "fg2" } # Fields of composite data types (e.g. structs, unions).
|
||||
|
||||
"label" = { fg = "magenta-bright" } # lifetimes - Loop labels, among other things.
|
||||
|
||||
"punctuation" = { fg = "fg2" } # Any punctuation symbol.
|
||||
# "punctuation.delimiter" = { fg = "fg2" } # Commas, colons or other delimiter depending on the language.
|
||||
# "punctuation.bracket" = { fg = "fg2" } # Parentheses, angle brackets, etc.
|
||||
# "punctuation.special" = { fg = "fg2" } # String interpolation brackets
|
||||
|
||||
"keyword" = { fg = "magenta" } # Language reserved keywords.
|
||||
"keyword.control" = { fg = "pink" } # Control keywords.
|
||||
"keyword.control.conditional" = { fg = "magenta-bright" } # `if`, `else`, `elif`.
|
||||
"keyword.control.repeat" = { fg = "magenta-bright" } # `for`, `while`, `loop`.
|
||||
"keyword.control.import" = { fg = "pink-bright" } # `import`, `export` `use`.
|
||||
"keyword.control.return" = { fg = "magenta" } # `return` in most languages.
|
||||
"keyword.control.exception" = { fg = "magenta" } # `try`, `catch`, `raise`/`throw` and related.
|
||||
"keyword.operator" = { fg = "fg2", modifiers = ["bold"] } # 'or', 'and', 'in'.
|
||||
"keyword.directive" = { fg = "pink-bright" } # Preprocessor directives (#if in C...).
|
||||
"keyword.function" = { fg = "red" } # The keyword to define a funtion: 'def', 'fun', 'fn'.
|
||||
"keyword.storage" = { fg = "magenta" } # Keywords describing how things are stored
|
||||
"keyword.storage.type" = { fg = "magenta" } # The type of something, class, function, var, let, etc.
|
||||
"keyword.storage.modifier" = { fg = "yellow" } # Storage modifiers like static, mut, const, ref, etc.
|
||||
|
||||
"operator" = { fg = "fg2" } # Logical, mathematical, and other operators.
|
||||
|
||||
"function" = { fg = "blue-bright" }
|
||||
"function.builtin" = { fg = "red" }
|
||||
"function.macro" = { fg = "red" }
|
||||
# "function.special" = { fg = "blue-bright" } # Preprocessor function in C.
|
||||
# "function.method" = { fg = "blue-bright" } # Class / Struct methods.
|
||||
|
||||
"tag" = { fg = "blue-bright" } # As in <body> for html, css tags.
|
||||
|
||||
"namespace" = { fg = "cyan-bright" } # Namespace or module identifier.
|
||||
|
||||
|
||||
# Diff ==============================
|
||||
# Version control changes.
|
||||
|
||||
"diff.plus" = "green-dim" # Additions.
|
||||
"diff.minus" = "red-dim" # Deletions.
|
||||
"diff.delta" = "blue-dim" # Modifications.
|
||||
"diff.delta.moved" = "cyan-dim" # Renamed or moved files.
|
||||
|
||||
# color palette
|
||||
[palette]
|
||||
black = "#393b44"
|
||||
red = "#c94f6d"
|
||||
red-dim = "#2f2837"
|
||||
green = "#81b29a"
|
||||
green-dim = "#26343c"
|
||||
yellow = "#dbc074"
|
||||
yellow-bright = "#e0c989"
|
||||
blue = "#719cd6"
|
||||
blue-bright = "#86abdc"
|
||||
blue-dim = "#2f2837"
|
||||
magenta = "#9d79d6"
|
||||
magenta-bright = "#baa1e2"
|
||||
cyan = "#63cdcf"
|
||||
cyan-bright = "#7ad4d6"
|
||||
cyan-dim = "#253f4a"
|
||||
white = "#dfdfe0"
|
||||
orange = "#f4a261"
|
||||
orange-bright = "#f6b079"
|
||||
pink = "#d67ad2"
|
||||
pink-bright = "#dc8ed9"
|
||||
comment = "#738091"
|
||||
# spec
|
||||
bg0 = "#131a24" # Dark bg (status line and float)
|
||||
bg1 = "#192330" # Default bg
|
||||
bg2 = "#212e3f" # Lighter bg (colorcolm folds)
|
||||
bg3 = "#29394f" # Lighter bg (cursor line)
|
||||
bg4 = "#39506d" # Conceal, border fg
|
||||
fg0 = "#d6d6d7" # Lighter fg
|
||||
fg1 = "#cdcecf" # Default fg
|
||||
fg2 = "#aeafb0" # Darker fg (status line)
|
||||
fg3 = "#71839b" # Darker fg (line numbers, fold colums)
|
||||
sel0 = "#2b3b51" # Popup bg, visual selection bg
|
||||
sel1 = "#3c5372" # Popup sel bg, search bg
|
@ -0,0 +1,60 @@
|
||||
# A unofficial port of VIM's zenburn theme: https://github.com/jnurmine/Zenburn/
|
||||
|
||||
"ui.background" = { bg = "bg" }
|
||||
"ui.menu" = { fg = "#9f9f9f", bg = "uibg" }
|
||||
"ui.menu.selected" = { fg = "#d0d0a0", bg = "#242424", modifiers = ["bold"] }
|
||||
"ui.linenr" = { fg = "#9fafaf", bg = "#262626"}
|
||||
"ui.linenr.selected" = { modifiers = ["bold"]}
|
||||
"ui.popup" = { bg = "uibg" }
|
||||
"ui.selection" = { bg = "#2f2f2f" }
|
||||
"comment" = { fg = "#7f9f7f" }
|
||||
"comment.block.documentation" = { fg = "black", modifiers = ["bold"] }
|
||||
"ui.statusline" = { bg = "statusbg", fg = "#ccdc90" }
|
||||
"ui.statusline.inactive" = { fg = '#2e3330', bg = '#88b090' }
|
||||
"ui.cursor" = { fg = "#000d18", bg = "#8faf9f", modifiers = ["bold"] }
|
||||
"ui.text" = { fg = "normal"}
|
||||
"operator" = { fg = "#f0efd0" , modifiers = []}
|
||||
"variable" = "normal"
|
||||
"variable.builtin" = {fg = "constant", modifiers = ["bold"]}
|
||||
"constant.numeric" = "numeric"
|
||||
"constant" = { fg = "constant", modifiers = ["bold"] }
|
||||
"type" = { fg = "#dfdfbf", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { fg = "#343434", bg = "#284f28", modifiers = ["bold"] }
|
||||
"string" = "#cc9393"
|
||||
"variable.other.member" = "#efef8f"
|
||||
"constant.character.escape" = { fg = "#dca3a3", modifiers = ["bold"]}
|
||||
"function" = "#efef8f"
|
||||
"function.macro" = { fg = "#ffcfaf", modifiers = ["bold"] }
|
||||
"special" = "#cfbfaf"
|
||||
"keyword" = { fg = "#f0dfaf", modifiers = ["bold"]}
|
||||
"keyword.storage-class" = { fg = "#c3bf9f", modifiers = ["bold"]}
|
||||
"label" = { fg = "#dfcfaf", modifiers = ["underlined"] }
|
||||
"ui.help" = { fg = "white", bg = "black" }
|
||||
"ui.virtual.ruler" = { bg = "#484848" }
|
||||
"ui.virtual.whitespace" = { fg = "#5b605e", modifiers = ["bold"]}
|
||||
|
||||
"punctuation.delimiter" = "#8f8f8f"
|
||||
|
||||
"ui.virtual.indent-guide" = "#4f4f4f"
|
||||
|
||||
"diff.plus" = {fg = "#709080", bg = "#313c36", modifiers = ["bold"] }
|
||||
"diff.delta" = "#333333"
|
||||
"diff.minus" = {fg = "#333333", bg = "#464646"}
|
||||
|
||||
"diagnostic" = {bg = "statusbg"}
|
||||
"diagnostic.error" = { fg = "errorfg", bg = "errorbg"}
|
||||
"ui.gutter" = { bg = "statusbg" }
|
||||
"hint" = {fg = "numeric", bg = "statusbg"}
|
||||
"warning" = "numeric"
|
||||
"error" = "errorfg"
|
||||
|
||||
[palette]
|
||||
bg = "#3f3f3f"
|
||||
uibg = "#2c2e2e"
|
||||
constant = "#dca3a3"
|
||||
normal = "#dcdccc"
|
||||
todo = "#dfdfdf"
|
||||
errorfg = "#e37170"
|
||||
errorbg = "#3d3535"
|
||||
statusbg = "#313633"
|
||||
numeric = "#8cd0d3"
|
Loading…
Reference in New Issue