summaryrefslogtreecommitdiff
path: root/tools/astro-languageserver/src/plugins/css/CSSDocument.ts
blob: 9f1839678a19619c997db89d69815e14543b2b55 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { Stylesheet, TextDocument } from 'vscode-css-languageservice';
import { Position } from 'vscode-languageserver';
import { getLanguageService } from './service';
import { Document, DocumentMapper, ReadableDocument, TagInformation } from '../../core/documents/index';

export interface CSSDocumentBase extends DocumentMapper, TextDocument {
  languageId: string;
  stylesheet: Stylesheet;
}

export class CSSDocument extends ReadableDocument implements DocumentMapper {
  private styleInfo: Pick<TagInformation, 'attributes' | 'start' | 'end'>;
  readonly version = this.parent.version;

  public stylesheet: Stylesheet;
  public languageId: string;

  constructor(private parent: Document) {
    super();

    if (this.parent.styleInfo) {
      this.styleInfo = this.parent.styleInfo;
    } else {
      this.styleInfo = {
        attributes: {},
        start: -1,
        end: -1,
      };
    }

    this.languageId = this.language;
    this.stylesheet = getLanguageService(this.language).parseStylesheet(this);
  }

  /**
   * Get the fragment position relative to the parent
   * @param pos Position in fragment
   */
  getOriginalPosition(pos: Position): Position {
    const parentOffset = this.styleInfo.start + this.offsetAt(pos);
    return this.parent.positionAt(parentOffset);
  }

  /**
   * Get the position relative to the start of the fragment
   * @param pos Position in parent
   */
  getGeneratedPosition(pos: Position): Position {
    const fragmentOffset = this.parent.offsetAt(pos) - this.styleInfo.start;
    return this.positionAt(fragmentOffset);
  }

  /**
   * Returns true if the given parent position is inside of this fragment
   * @param pos Position in parent
   */
  isInGenerated(pos: Position): boolean {
    const offset = this.parent.offsetAt(pos);
    return offset >= this.styleInfo.start && offset <= this.styleInfo.end;
  }

  /**
   * Get the fragment text from the parent
   */
  getText(): string {
    return this.parent.getText().slice(this.styleInfo.start, this.styleInfo.end);
  }

  /**
   * Returns the length of the fragment as calculated from the start and end positon
   */
  getTextLength(): number {
    return this.styleInfo.end - this.styleInfo.start;
  }

  /**
   * Return the parent file path
   */
  getFilePath(): string | null {
    return this.parent.getFilePath();
  }

  getURL() {
    return this.parent.getURL();
  }

  getAttributes() {
    return this.styleInfo.attributes;
  }

  private get language() {
    const attrs = this.getAttributes();
    return attrs.lang || attrs.type || 'css';
  }
}