diff --git a/orgize-lsp/editors/vscode/package.json b/orgize-lsp/editors/vscode/package.json index 586291d..8b96d37 100644 --- a/orgize-lsp/editors/vscode/package.json +++ b/orgize-lsp/editors/vscode/package.json @@ -75,6 +75,29 @@ } } ], + "semanticTokenScopes": [ + { + "language": "org", + "scopes": { + "headlineTodoKeyword": [ + "invalid.illegal.org" + ], + "headlineDoneKeyword": [ + "keyword.control.org" + ], + "headlineTags": [ + "markup.italic.org", + "keyword.control.org" + ], + "headlinePriority": [ + "keyword.control.org" + ], + "timestamp": [ + "variable.org" + ] + } + } + ], "grammars": [ { "language": "org", diff --git a/orgize-lsp/editors/vscode/syntaxes/org.tmLanguage.json b/orgize-lsp/editors/vscode/syntaxes/org.tmLanguage.json index f3df40d..7c9e0f9 100644 --- a/orgize-lsp/editors/vscode/syntaxes/org.tmLanguage.json +++ b/orgize-lsp/editors/vscode/syntaxes/org.tmLanguage.json @@ -1863,9 +1863,6 @@ }, "common": { "patterns": [ - { - "include": "#timestamp" - }, { "include": "#link" }, @@ -1899,23 +1896,6 @@ "patterns": [ { "include": "#common" - }, - { - "include": "#todo" - }, - { - "include": "#done" - }, - { - "include": "#userKeywords" - } - ] - }, - "timestamp": { - "patterns": [ - { - "name": "variable.org", - "match": "\\[\\d{4}-\\d{1,2}-\\d{1,2}(?: \\w{3})?\\]" } ] }, @@ -1953,30 +1933,6 @@ } ] }, - "todo": { - "patterns": [ - { - "name": "invalid.illegal.org", - "match": "\\bTODO\\b" - } - ] - }, - "done": { - "patterns": [ - { - "name": "keyword.control.org", - "match": "\\bDONE\\b" - } - ] - }, - "userKeywords": { - "patterns": [ - { - "name": "string.quoted.double.org", - "match": "\\b([A-Z]{3,})\\b" - } - ] - }, "bold": { "patterns": [ { diff --git a/orgize-lsp/src/main.rs b/orgize-lsp/src/main.rs index be84ccf..8510de2 100644 --- a/orgize-lsp/src/main.rs +++ b/orgize-lsp/src/main.rs @@ -64,8 +64,8 @@ impl LanguageServer for Backend { semantic_tokens_options: SemanticTokensOptions { work_done_progress_options: WorkDoneProgressOptions::default(), legend: SemanticTokensLegend { - token_types: LEGEND_TYPE.into(), - token_modifiers: vec![], + token_types: semantic_token::TYPES.into(), + token_modifiers: semantic_token::MODIFIERS.into(), }, range: Some(true), full: Some(SemanticTokensFullOptions::Bool(true)), diff --git a/orgize-lsp/src/semantic_token.rs b/orgize-lsp/src/semantic_token.rs index de75fdd..c090f0f 100644 --- a/orgize-lsp/src/semantic_token.rs +++ b/orgize-lsp/src/semantic_token.rs @@ -1,13 +1,27 @@ use orgize::{ export::{Container, Event, TraversalContext, Traverser}, rowan::{ast::AstNode, TextRange}, + SyntaxKind, }; -use tower_lsp::lsp_types::{Range, SemanticToken, SemanticTokenType}; +use tower_lsp::lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType}; use crate::org_document::OrgDocument; -/// Semantic token types that are used for highlighting -pub const LEGEND_TYPE: &[SemanticTokenType] = &[SemanticTokenType::COMMENT]; +const TIMESTAMP: SemanticTokenType = SemanticTokenType::new("timestamp"); +const HEADLINE_TODO_KEYWORD: SemanticTokenType = SemanticTokenType::new("headlineTodoKeyword"); +const HEADLINE_DONE_KEYWORD: SemanticTokenType = SemanticTokenType::new("headlineDoneKeyword"); +const HEADLINE_PRIORITY: SemanticTokenType = SemanticTokenType::new("headlinePriority"); +const HEADLINE_TAGS: SemanticTokenType = SemanticTokenType::new("headlineTags"); + +pub const TYPES: &[SemanticTokenType] = &[ + TIMESTAMP, + HEADLINE_TODO_KEYWORD, + HEADLINE_DONE_KEYWORD, + HEADLINE_PRIORITY, + HEADLINE_TAGS, +]; + +pub const MODIFIERS: &[SemanticTokenModifier] = &[]; pub struct SemanticTokenTraverser<'a> { pub doc: &'a OrgDocument, @@ -21,37 +35,54 @@ pub struct SemanticTokenTraverser<'a> { impl<'a> Traverser for SemanticTokenTraverser<'a> { fn event(&mut self, event: Event, ctx: &mut TraversalContext) { + macro_rules! m { + ($range:expr, $ty:expr $(,$modifiers:expr)*) => {{ + if let Some(token) = + self.create_token($range.start().into(), $range.end().into(), $ty) + { + self.tokens.push(token); + } + }}; + } + + macro_rules! s { + ($range:expr) => { + if let Some(range) = self.range { + if !range.contains_range($range) { + return ctx.skip(); + } + } + }; + } + match event { - Event::Enter(Container::Comment(comment)) => { - let range = comment.syntax().text_range(); + Event::Enter(Container::Section(section)) => s!(section.syntax().text_range()), + Event::Enter(Container::Paragraph(paragraph)) => s!(paragraph.syntax().text_range()), + Event::Enter(Container::OrgTable(table)) => s!(table.syntax().text_range()), + Event::Enter(Container::List(list)) => s!(list.syntax().text_range()), + Event::Enter(Container::Drawer(drawer)) => s!(drawer.syntax().text_range()), + Event::Enter(Container::DynBlock(block)) => s!(block.syntax().text_range()), - if self.contains_range(range) { - if let Some(token) = self.create_token( - comment.begin(), - comment.end(), - SemanticTokenType::COMMENT, - ) { - self.tokens.push(token); + Event::Enter(Container::Headline(headline)) => { + s!(headline.syntax().text_range()); + + for ch in headline.syntax().children_with_tokens() { + match ch.kind() { + SyntaxKind::HEADLINE_KEYWORD_DONE => { + m!(ch.text_range(), HEADLINE_DONE_KEYWORD) + } + SyntaxKind::HEADLINE_KEYWORD_TODO => { + m!(ch.text_range(), HEADLINE_TODO_KEYWORD) + } + SyntaxKind::HEADLINE_TAGS => m!(ch.text_range(), HEADLINE_TAGS), + SyntaxKind::HEADLINE_PRIORITY => m!(ch.text_range(), HEADLINE_PRIORITY), + SyntaxKind::NEW_LINE => break, + _ => {} } } - - ctx.skip(); } - Event::Enter(Container::CommentBlock(comment)) => { - let range = comment.syntax().text_range(); - if self.contains_range(range) { - if let Some(token) = self.create_token( - comment.begin(), - comment.end(), - SemanticTokenType::COMMENT, - ) { - self.tokens.push(token); - } - } - - ctx.skip(); - } + Event::Timestamp(timestamp) => m!(timestamp.syntax().text_range(), TIMESTAMP), _ => {} } @@ -82,13 +113,6 @@ impl<'a> SemanticTokenTraverser<'a> { } } - fn contains_range(&self, range: TextRange) -> bool { - match self.range { - Some(r) => r.contains_range(range), - None => true, - } - } - fn create_token( &mut self, start: u32, @@ -96,11 +120,11 @@ impl<'a> SemanticTokenTraverser<'a> { kind: SemanticTokenType, ) -> Option { let length = end - start; - let token_type = LEGEND_TYPE.iter().position(|item| item == &kind)? as u32; + let token_type = TYPES.iter().position(|item| item == &kind)? as u32; let line = self.doc.line_of(start); - let first = self.doc.line_of(line); - let start = self.doc.line_of(start) - first; + + let start = start - self.doc.line_starts[line as usize]; let delta_line = line - self.previous_line; let delta_start = if delta_line == 0 {