yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
3485710e9
master
1// slang-doc-extractor.cpp 2#include "slang-doc-extractor.h" 3 4#include "../core/slang-string-util.h" 5 6namespace Slang 7{ 8 9/* TODO(JS): 10 11* If Decls hand SourceRange, then we could use the range to simplify getting the Post markup, as 12will be trivial to get to the 'end' 13* Need to handle preceeding * in some markup styles 14* If we want to be able to disable markup we need a mechanism to do this. Probably define source 15ranges. 16 17* Need a way to take the extracted markup and produce suitable markdown 18** This will need to display the decoration appropriately 19*/ 20 21/* static */ UnownedStringSlice DocMarkupExtractor ::removeStart ( 22MarkupType type , 23const UnownedStringSlice & comment ) 24{ 25switch (type ) 26 { 27case MarkupType ::BlockBefore : 28 { 29if (comment .startsWith (UnownedStringSlice ::fromLiteral ("/**" ))|| 30comment .startsWith (UnownedStringSlice ::fromLiteral ("/*!" ))) 31 { 32/// /** */ or /*! */. 33return comment .tail (3 ); 34 } 35return comment ; 36 } 37case MarkupType ::BlockAfter : 38 { 39 40if (comment .startsWith (UnownedStringSlice ::fromLiteral ("/**<" ))|| 41comment .startsWith (UnownedStringSlice ::fromLiteral ("/*!<" ))) 42 { 43/// /*!< */ or /**< */ 44return comment .tail (4 ); 45 } 46return comment ; 47 } 48case MarkupType ::OrdinaryBlockBefore : 49 { 50if (comment .startsWith (UnownedStringSlice ::fromLiteral ("/*" ))) 51 { 52/// ordinary /* */ block. 53return comment .tail (2 ); 54 } 55return comment ; 56 } 57case MarkupType ::LineBangBefore : 58 { 59return comment .startsWith (UnownedStringSlice ::fromLiteral ("//!" )) ?comment .tail (3 ) 60 :comment ; 61 } 62case MarkupType ::LineSlashBefore : 63 { 64return comment .startsWith (UnownedStringSlice ::fromLiteral ("///" )) ?comment .tail (3 ) 65 :comment ; 66 } 67case MarkupType ::OrdinaryLineBefore : 68case MarkupType ::OrdinaryLineAfter : 69 { 70return comment .startsWith (UnownedStringSlice ::fromLiteral ("//" )) ?comment .tail (2 ) 71 :comment ; 72 } 73case MarkupType ::LineBangAfter : 74 { 75/// //!< Can be multiple lines 76return comment .startsWith (UnownedStringSlice ::fromLiteral ("//!<" )) ?comment .tail (4 ) 77 :comment ; 78 } 79case MarkupType ::LineSlashAfter : 80 { 81return comment .startsWith (UnownedStringSlice ::fromLiteral ("///<" )) ?comment .tail (4 ) 82 :comment ; 83 } 84default : 85break ; 86 } 87return comment ; 88} 89 90static Index _findTokenIndex (SourceLoc loc ,const Token * toks ,Index numToks ) 91{ 92// Use a binary search to find the token 93Index lo = 0 ; 94Index hi = numToks ; 95 96while (lo + 1 < hi ) 97 { 98const Index mid = (hi + lo ) >>1 ; 99const Token & midToken = toks [mid ]; 100 101if (midToken .loc == loc ) 102 { 103return mid ; 104 } 105 106if (midToken .loc .getRaw () <=loc .getRaw ()) 107 { 108lo = mid ; 109 } 110else 111 { 112hi = mid ; 113 } 114 } 115 116// Not found 117return -1 ; 118} 119 120/* static */ DocMarkupExtractor ::MarkupFlags DocMarkupExtractor ::getFlags (MarkupType type ) 121{ 122switch (type ) 123 { 124default : 125case MarkupType ::None : 126return 0 ; 127case MarkupType ::BlockBefore : 128return MarkupFlag ::Before |MarkupFlag ::IsBlock ; 129case MarkupType ::BlockAfter : 130return MarkupFlag ::After |MarkupFlag ::IsBlock ; 131case MarkupType ::OrdinaryBlockBefore : 132return MarkupFlag ::Before |MarkupFlag ::IsBlock ; 133 134case MarkupType ::LineBangBefore : 135return MarkupFlag ::Before |MarkupFlag ::IsMultiToken ; 136case MarkupType ::LineSlashBefore : 137return MarkupFlag ::Before |MarkupFlag ::IsMultiToken ; 138case MarkupType ::OrdinaryLineBefore : 139return MarkupFlag ::Before |MarkupFlag ::IsMultiToken ; 140 141case MarkupType ::LineBangAfter : 142return MarkupFlag ::After |MarkupFlag ::IsMultiToken ; 143case MarkupType ::LineSlashAfter : 144return MarkupFlag ::After |MarkupFlag ::IsMultiToken ; 145case MarkupType ::OrdinaryLineAfter : 146return MarkupFlag ::After |MarkupFlag ::IsMultiToken ; 147 } 148} 149 150/* static */ DocMarkupExtractor ::MarkupType DocMarkupExtractor ::findMarkupType (const Token & tok ) 151{ 152switch (tok .type ) 153 { 154case TokenType ::BlockComment : 155 { 156UnownedStringSlice slice = tok .getContent (); 157if (slice .getLength () >=3 && (slice [2 ]== '!' || slice [2 ]== '*' )) 158 { 159return (slice .getLength () >=4 && slice [3 ]== '<' ) ?MarkupType ::BlockAfter 160 :MarkupType ::BlockBefore ; 161 } 162else 163 { 164return MarkupType ::OrdinaryBlockBefore ; 165 } 166break ; 167 } 168case TokenType ::LineComment : 169 { 170UnownedStringSlice slice = tok .getContent (); 171if (slice .getLength () >=3 ) 172 { 173if (slice [2 ]== '!' ) 174 { 175return (slice .getLength () >=4 && slice [3 ]== '<' ) ?MarkupType ::LineBangAfter 176 :MarkupType ::LineBangBefore ; 177 } 178else if (slice [2 ]== '/' ) 179 { 180return (slice .getLength () >=4 && slice [3 ]== '<' ) 181 ?MarkupType ::LineSlashAfter 182 :MarkupType ::LineSlashBefore ; 183 } 184 } 185return (tok .flags & TokenFlag ::AtStartOfLine )!= 0 ?MarkupType ::OrdinaryLineBefore 186 :MarkupType ::OrdinaryLineAfter ; 187break ; 188 } 189default : 190break ; 191 } 192return MarkupType ::None ; 193} 194 195static Index _calcWhitespaceIndent (const UnownedStringSlice & line ) 196{ 197// TODO(JS): For now we ignore tabs and just work out indentation based on spaces/assume ASCII 198Index indent = 0 ; 199const Index count = line .getLength (); 200for (;indent < count && line [indent ]== ' ' ;indent ++ ) 201 ; 202return indent ; 203} 204 205static Index _calcIndent (const UnownedStringSlice & line ) 206{ 207// TODO(JS): For now we just assume no tabs, and that every char is ASCII 208return line .getLength (); 209} 210 211static void _appendUnindenttedLine ( 212const UnownedStringSlice & line , 213Index maxIndent , 214StringBuilder & out ) 215{ 216Index indent = _calcWhitespaceIndent (line ); 217 218// We want to remove indenting remove no more than maxIndent 219if (maxIndent >=0 ) 220 { 221indent = (indent > maxIndent ) ?maxIndent :indent ; 222 } 223 224// Remove the indenting, and append to out 225out .append (line .tail (indent )); 226} 227 228SlangResult DocMarkupExtractor ::_extractMarkup ( 229const FindInfo & info , 230const FoundMarkup & foundMarkup , 231StringBuilder & out ) 232{ 233SourceView * sourceView = info .sourceView ; 234SourceFile * sourceFile = sourceView -> getSourceFile (); 235 236// Here we want to produce the text that is implied by the markup tokens. 237// We want to removing surrounding markup, and to also keep appropriate indentation 238 239switch (foundMarkup .type ) 240 { 241case MarkupType ::BlockBefore : 242case MarkupType ::BlockAfter : 243case MarkupType ::OrdinaryBlockBefore : 244 { 245// We should only have a single line 246SLANG_ASSERT (foundMarkup .range .getCount ()== 1 ); 247 248const auto & tok = info .tokenList -> m_tokens [foundMarkup .range .start ]; 249uint32_t offset = sourceView -> getRange ().getOffset (tok .loc ); 250 251const UnownedStringSlice startLine = sourceFile -> getLineContainingOffset (offset ); 252 253UnownedStringSlice content = tok .getContent (); 254 255// Split into lines 256List < UnownedStringSlice > lines ; 257 258StringUtil ::calcLines (content ,lines ); 259 260Index maxIndent = -1 ; 261 262StringBuilder unindentedLine ; 263 264const Index linesCount = lines .getCount (); 265for (Index i = 0 ;i < linesCount ;++ i ) 266 { 267UnownedStringSlice line = lines [i ]; 268unindentedLine .clear (); 269 270if (i == 0 ) 271 { 272if (startLine .isMemoryContained (line .begin ())) 273 { 274// For now we'll ignore tabs, and that the indent amount is, the amount of 275// *byte* NOTE! This is only appropriate for ASCII without tabs. 276maxIndent = 277_calcIndent (UnownedStringSlice (startLine .begin (),line .begin ())); 278 279// Let's strip the start stuff 280line = removeStart (foundMarkup .type ,line ); 281 } 282 } 283 284if (i == linesCount - 1 ) 285 { 286SLANG_ASSERT ( 287line .tail (line .getLength ()- 2 )== UnownedStringSlice ::fromLiteral ("*/" )); 288// Remove the */ at the end of the line 289line = line .head (line .getLength ()- 2 ); 290 } 291 292if (i > 0 ) 293 { 294_appendUnindenttedLine (line ,maxIndent ,unindentedLine ); 295 } 296else 297 { 298unindentedLine .append (line ); 299 } 300 301// If the first or last line are all white space, just ignore them 302if ((i == linesCount - 1 || i == 0 )&& 303unindentedLine .getUnownedSlice ().trim ().getLength ()== 0 ) 304 { 305continue ; 306 } 307 308out .append (unindentedLine ); 309out .appendChar ('\n' ); 310 } 311 312break ; 313 } 314case MarkupType ::OrdinaryLineBefore : 315case MarkupType ::OrdinaryLineAfter : 316case MarkupType ::LineBangBefore : 317case MarkupType ::LineSlashBefore : 318case MarkupType ::LineBangAfter : 319case MarkupType ::LineSlashAfter : 320 { 321// Holds the lines extracted, they may have some white space indenting (like the space 322// at the start of //) 323List < UnownedStringSlice > lines ; 324 325const auto & range = foundMarkup .range ; 326for (Index i = range .start ;i < range .end ;++ i ) 327 { 328const auto & tok = info .tokenList -> m_tokens [i ]; 329UnownedStringSlice line = tok .getContent (); 330line = removeStart (foundMarkup .type ,line ); 331 332// If the first or last line are all white space, just ignore them 333if ((i == range .start || i == range .end - 1 )&& line .trim ().getLength ()== 0 ) 334 { 335continue ; 336 } 337lines .add (line ); 338 } 339 340if (lines .getCount ()== 0 ) 341 { 342// If there are no lines, theres no content 343return SLANG_OK ; 344 } 345 346Index minIndent = 0x7fffffff ; 347for (const auto & line :lines ) 348 { 349const Index indent = _calcWhitespaceIndent (line ); 350minIndent = (indent < minIndent ) ?indent :minIndent ; 351 } 352 353for (const auto & line :lines ) 354 { 355_appendUnindenttedLine (line ,minIndent ,out ); 356out .appendChar ('\n' ); 357 } 358 359break ; 360 } 361default : 362return SLANG_FAIL ; 363 } 364 365return SLANG_OK ; 366} 367 368Index DocMarkupExtractor ::_findStartIndex (const FindInfo & info ,Location location ) 369{ 370Index openCount = 0 ; 371 372const TokenList & toks = * info .tokenList ; 373const Index tokIndex = info .tokenIndex ; 374 375Index direction = isBefore (location ) ?-1 :1 ; 376 377const Index count = toks .m_tokens .getCount (); 378for (Index i = tokIndex ;i >=0 && i < count ;i += direction ) 379 { 380const Token & tok = toks .m_tokens [i ]; 381 382switch (tok .type ) 383 { 384case TokenType ::LBrace : 385case TokenType ::LBracket : 386case TokenType ::LParent : 387case TokenType ::OpLess : 388 { 389openCount += direction ; 390if (openCount < 0 ) 391return -1 ; 392break ; 393 } 394case TokenType ::RBracket : 395 { 396openCount -= direction ; 397if (openCount < 0 ) 398return -1 ; 399break ; 400 } 401case TokenType ::OpGreater : 402 { 403if (location == Location ::AfterGenericParam && openCount == 0 ) 404 { 405return i + 1 ; 406 } 407 408openCount -= direction ; 409if (openCount < 0 ) 410return -1 ; 411 412break ; 413 } 414case TokenType ::RParent : 415 { 416if (openCount == 0 && location == Location ::AfterParam ) 417 { 418return i + 1 ; 419 } 420 421openCount -= direction ; 422if (openCount < 0 ) 423return -1 ; 424break ; 425 } 426case TokenType ::RBrace : 427 { 428// If we haven't hit a candidate yet before hitting } it's not going to work 429if (location == Location ::Before || location == Location ::AfterEnumCase ) 430 { 431return -1 ; 432 } 433break ; 434 } 435case TokenType ::BlockComment : 436case TokenType ::LineComment : 437 { 438if (openCount == 0 ) 439 { 440// Determine the markup type 441const MarkupType markupType = findMarkupType (tok ); 442if (!m_searchInOrindaryComments && 443 (markupType == MarkupType ::OrdinaryBlockBefore || 444markupType == MarkupType ::OrdinaryLineBefore )) 445break ; 446// If the location wanted is before and the markup is, we'll assume this is it 447if (isBefore (location )&& isBefore (markupType )) 448 { 449return i ; 450 } 451// If we are looking for enum cases, and the markup is after, we'll assume this 452// is it 453if (isAfter (location )&& isAfter (markupType )) 454 { 455return i ; 456 } 457 } 458break ; 459 } 460case TokenType ::Comma : 461 { 462if (openCount == 0 ) 463 { 464if (location == Location ::AfterParam || location == Location ::AfterEnumCase || 465location == Location ::AfterGenericParam ) 466 { 467return i + 1 ; 468 } 469 470if (location == Location ::Before ) 471 { 472return -1 ; 473 } 474 } 475 476break ; 477 } 478case TokenType ::Semicolon : 479 { 480// If we haven't hit a candidate yet it's not going to work 481if (location == Location ::Before ) 482 { 483return -1 ; 484 } 485if (openCount == 0 && location == Location ::AfterSemicolon ) 486 { 487return i + 1 ; 488 } 489break ; 490 } 491default : 492break ; 493 } 494 } 495 496return -1 ; 497} 498 499/* static */ bool DocMarkupExtractor ::_isTokenOnLineIndex ( 500SourceView * sourceView , 501MarkupType type , 502const Token & tok , 503Index lineIndex ) 504{ 505SourceFile * sourceFile = sourceView -> getSourceFile (); 506const int offset = sourceView -> getRange ().getOffset (tok .loc ); 507 508auto const flags = getFlags (type ); 509 510if (flags & MarkupFlag ::IsBlock ) 511 { 512// Either the start or the end of the block have to be on the specified line 513return sourceFile -> isOffsetOnLine (offset ,lineIndex )|| 514sourceFile -> isOffsetOnLine (offset + tok .charsCount ,lineIndex ); 515 } 516else 517 { 518// Has to be exactly on the specified line 519return sourceFile -> isOffsetOnLine (offset ,lineIndex ); 520 } 521} 522 523SlangResult DocMarkupExtractor ::_findMarkup ( 524const FindInfo & info , 525Location location , 526FoundMarkup & out ) 527{ 528out .reset (); 529 530const auto & toks = info .tokenList -> m_tokens ; 531 532// The starting token index 533Index startIndex = _findStartIndex (info ,location ); 534if (startIndex <=0 ) 535 { 536return SLANG_E_NOT_FOUND ; 537 } 538 539SourceView * sourceView = info .sourceView ; 540SourceFile * sourceFile = sourceView -> getSourceFile (); 541 542// Let's lookup the line index where this occurred 543const int startOffset = sourceView -> getRange ().getOffset (toks [startIndex ].loc ); 544 545// The line index that the markoff starts from 546Index lineIndex = sourceFile -> calcLineIndexFromOffset (startOffset ); 547if (lineIndex < 0 ) 548 { 549return SLANG_E_NOT_FOUND ; 550 } 551 552const Index searchDirection = isBefore (location ) ?-1 :1 ; 553 554// Get the type and flags 555const MarkupType type = findMarkupType (toks [startIndex ]); 556const MarkupFlags flags = getFlags (type ); 557 558const MarkupFlag ::Enum requiredFlag = 559isBefore (location ) ?MarkupFlag ::Before :MarkupFlag ::After ; 560if ((flags & requiredFlag )== 0 ) 561 { 562return SLANG_E_NOT_FOUND ; 563 } 564 565#if 0 566// The token still isn't accepted, unless it's on the expected line 567if (_isTokenOnLineIndex (info .sourceView ,type ,toks [startIndex ],expectedLineIndex )) 568 { 569return SLANG_E_NOT_FOUND ; 570 } 571#endif 572 573Index endIndex = startIndex ; 574 575// If it's multiline, so look for the end index 576if (flags & MarkupFlag ::IsMultiToken ) 577 { 578Index expectedLineIndex = lineIndex ; 579 580// TODO(JS): 581// We should probably do the work here to confirm indentation - but that 582// requires knowing something about tabs, so for now we leave. 583 584while (true) 585 { 586endIndex += searchDirection ; 587expectedLineIndex += searchDirection ; 588if (expectedLineIndex < 0 ) 589break ; 590if (endIndex < 0 || endIndex >=toks .getCount ()) 591 { 592break ; 593 } 594 595// Do we find a token of the right type? 596if (findMarkupType (toks [endIndex ])!= type ) 597 { 598break ; 599 } 600 601// Is it on the right line? 602if (!_isTokenOnLineIndex (info .sourceView ,type ,toks [endIndex ],expectedLineIndex )) 603 { 604break ; 605 } 606 } 607 608// Fix the end index (it's the last one that worked) 609endIndex -= searchDirection ; 610 } 611 612// Put start < end order 613if (endIndex < startIndex ) 614 { 615Swap (endIndex ,startIndex ); 616 } 617// The range excludes end so increase 618endIndex ++ ; 619 620// Okay we've found the markup 621out .type = type ; 622out .location = location ; 623out .range = IndexRange {startIndex ,endIndex }; 624 625SLANG_ASSERT (out .range .getCount ()> 0 ); 626 627return SLANG_OK ; 628} 629 630SlangResult DocMarkupExtractor ::_findFirstMarkup ( 631const FindInfo & info , 632const Location * locs , 633Index locCount , 634FoundMarkup & out , 635Index & outIndex ) 636{ 637Index i = 0 ; 638for (;i < locCount ;++ i ) 639 { 640SlangResult res = _findMarkup (info ,locs [i ],out ); 641if (SLANG_SUCCEEDED (res )|| (SLANG_FAILED (res )&& res != SLANG_E_NOT_FOUND )) 642 { 643outIndex = i ; 644return res ; 645 } 646 } 647return SLANG_E_NOT_FOUND ; 648} 649 650SlangResult DocMarkupExtractor ::_findMarkup ( 651const FindInfo & info , 652const Location * locs , 653Index locCount , 654FoundMarkup & out ) 655{ 656Index foundIndex ; 657SLANG_RETURN_ON_FAIL (_findFirstMarkup (info ,locs ,locCount ,out ,foundIndex )); 658 659// Lets see if the remaining ones match 660 { 661FoundMarkup otherMarkup ; 662for (Index i = foundIndex + 1 ;i < locCount ;++ i ) 663 { 664SlangResult res = _findMarkup (info ,locs [i ],otherMarkup ); 665if (SLANG_SUCCEEDED (res )) 666 { 667// TODO(JS): Warning found markup in another location 668 } 669 } 670 } 671 672return SLANG_OK ; 673} 674 675 676SlangResult DocMarkupExtractor ::_findMarkup ( 677const FindInfo & info , 678SearchStyle searchStyle , 679FoundMarkup & out ) 680{ 681switch (searchStyle ) 682 { 683default : 684case SearchStyle ::None : 685 { 686return SLANG_E_NOT_FOUND ; 687 } 688case SearchStyle ::EnumCase : 689 { 690Location locs []= {Location ::Before ,Location ::AfterEnumCase }; 691return _findMarkup (info ,locs ,SLANG_COUNT_OF (locs ),out ); 692 } 693case SearchStyle ::Param : 694 { 695Location locs []= {Location ::Before ,Location ::AfterParam }; 696return _findMarkup (info ,locs ,SLANG_COUNT_OF (locs ),out ); 697 } 698case SearchStyle ::Before : 699 { 700return _findMarkup (info ,Location ::Before ,out ); 701 } 702case SearchStyle ::Function : 703 { 704return _findMarkup (info ,Location ::Before ,out ); 705 } 706case SearchStyle ::Attribute : 707 { 708FindInfo newInfo = info ; 709newInfo .tokenIndex -= 2 ; 710return _findMarkup (newInfo ,Location ::Before ,out ); 711 } 712case SearchStyle ::Variable : 713 { 714Location locs []= {Location ::Before ,Location ::AfterSemicolon }; 715return _findMarkup (info ,locs ,SLANG_COUNT_OF (locs ),out ); 716 } 717case SearchStyle ::GenericParam : 718 { 719Location locs []= {Location ::Before ,Location ::AfterGenericParam }; 720return _findMarkup (info ,locs ,SLANG_COUNT_OF (locs ),out ); 721 } 722 } 723} 724 725static void _calcLineVisibility ( 726SourceView * sourceView , 727const TokenList & toks , 728List < MarkupVisibility >& outLineVisibility ) 729{ 730SourceFile * sourceFile = sourceView -> getSourceFile (); 731const auto & lineOffsets = sourceFile -> getLineBreakOffsets (); 732 733outLineVisibility .setCount (lineOffsets .getCount ()+ 1 ); 734 735MarkupVisibility lastVisibility = MarkupVisibility ::Public ; 736Index lastLine = 0 ; 737 738for (const auto & tok :toks ) 739 { 740if (tok .type == TokenType ::LineComment ) 741 { 742UnownedStringSlice contents = tok .getContent (); 743 744MarkupVisibility newVisibility = lastVisibility ; 745 746// Distinct from other markup 747if (contents .startsWith (toSlice ("//@" ))) 748 { 749UnownedStringSlice access = contents .tail (3 ).trim (); 750if (access == "hidden:" || access == "private:" ) 751 { 752newVisibility = MarkupVisibility ::Hidden ; 753 } 754else if (access == "internal:" ) 755 { 756newVisibility = MarkupVisibility ::Internal ; 757 } 758else if (access == "public:" ) 759 { 760newVisibility = MarkupVisibility ::Public ; 761 } 762 } 763 764if (newVisibility != lastVisibility ) 765 { 766// Work up the line it's on 767const int offset = sourceView -> getRange ().getOffset (tok .loc ); 768Index line = sourceFile -> calcLineIndexFromOffset (offset ); 769 770// Fill in the span 771for (Index i = lastLine ;i < line ;++ i ) 772 { 773outLineVisibility [i ]= lastVisibility ; 774 } 775 776// Record the new access and where we are up to 777lastLine = line ; 778lastVisibility = newVisibility ; 779 } 780 } 781 } 782 783// Fill in the remaining 784for (Index i = lastLine ;i < outLineVisibility .getCount ();++ i ) 785 { 786outLineVisibility [i ]= lastVisibility ; 787 } 788} 789 790SlangResult DocMarkupExtractor ::extract ( 791const SearchItemInput * inputs , 792Index inputCount , 793SourceManager * sourceManager , 794DiagnosticSink * sink , 795List < SourceView *>& outViews , 796List < SearchItemOutput >& out ) 797{ 798struct Entry 799 { 800Index viewIndex ;///< The view/file index this loc is found in 801SourceLoc ::RawValue locOrOffset ;///< Can be a loc or an offset into the file 802 803SearchStyle searchStyle ;///< The search style when looking for an item 804Index inputIndex ;///< The index to this item in the input 805 }; 806 807List < Entry > entries ; 808 809 { 810entries .setCount (inputCount ); 811for (Index i = 0 ;i < inputCount ;++ i ) 812 { 813const auto & input = inputs [i ]; 814Entry & entry = entries [i ]; 815entry .inputIndex = i ; 816entry .viewIndex = -1 ;//< We don't know what file/view it's in 817entry .locOrOffset = input .sourceLoc .getRaw (); 818entry .searchStyle = input .searchStyle ; 819 } 820 } 821 822// Sort them into loc order 823entries .sort ( 824 [](const Entry & a ,const Entry & b )-> bool {return a .locOrOffset < b .locOrOffset ; }); 825 826 { 827SourceView * sourceView = nullptr ; 828Index viewIndex = -1 ; 829 830for (auto & entry :entries ) 831 { 832if (entry .searchStyle == SearchStyle ::None ) 833 { 834continue ; 835 } 836 837const SourceLoc loc = SourceLoc ::fromRaw (entry .locOrOffset ); 838 839if (sourceView == nullptr || !sourceView -> getRange ().contains (loc )) 840 { 841// Find the new view 842sourceView = sourceManager -> findSourceView (loc ); 843if (!sourceView ) 844 { 845entry .searchStyle = SearchStyle ::None ; 846continue ; 847 } 848 849// We want only one view per SourceFile 850SourceFile * sourceFile = sourceView -> getSourceFile (); 851 852// NOTE! The view found might be different than sourceView. 853viewIndex = outViews .findFirstIndex ( 854 [& ](SourceView * currentView )-> bool 855 {return currentView -> getSourceFile ()== sourceFile ; }); 856 857if (viewIndex < 0 ) 858 { 859viewIndex = outViews .getCount (); 860outViews .add (sourceView ); 861 } 862 } 863 864SLANG_ASSERT (viewIndex >=0 ); 865SLANG_ASSERT (sourceView && sourceView -> getRange ().contains (loc )); 866 867// Set the file index 868entry .viewIndex = viewIndex ; 869// Set as the offset within the file 870entry .locOrOffset = sourceView -> getRange ().getOffset (loc ); 871 } 872 873// Sort into view/file and then offset order 874entries .sort ( 875 [](const Entry & a ,const Entry & b )-> bool 876 { 877return (a .viewIndex < b .viewIndex )|| 878 ((a .viewIndex == b .viewIndex )&& a .locOrOffset < b .locOrOffset ); 879 }); 880 } 881 882 { 883TokenList tokens ; 884List < MarkupVisibility > lineVisibility ; 885 886MemoryArena memoryArena (4096 ); 887 888NamePool namePool ; 889 890Index viewIndex = -1 ; 891SourceView * sourceView = nullptr ; 892 893const Int entryCount = entries .getCount (); 894 895out .setCount (entryCount ); 896 897for (Index i = 0 ;i < entryCount ;++ i ) 898 { 899const auto & entry = entries [i ]; 900auto & dst = out [i ]; 901 902dst .viewIndex = -1 ; 903dst .inputIndex = entry .inputIndex ; 904dst .visibilty = MarkupVisibility ::Public ; 905 906// If there isn't a mechanism to search with, just move on 907if (entry .searchStyle == SearchStyle ::None ) 908 { 909continue ; 910 } 911 912if (viewIndex != entry .viewIndex ) 913 { 914viewIndex = entry .viewIndex ; 915sourceView = outViews [viewIndex ]; 916 917// Make all memory free again 918memoryArena .reset (); 919 920// Run the lexer 921Lexer lexer ; 922lexer .initialize (sourceView ,sink ,& namePool ,& memoryArena ); 923 924// Lex everything 925tokens = lexer .lexAllMarkupTokens (); 926 927// Let's work out the access 928 929_calcLineVisibility (sourceView ,tokens ,lineVisibility ); 930 } 931 932dst .viewIndex = viewIndex ; 933 934// Get the offset within the source file 935const uint32_t offset = entry .locOrOffset ; 936 937// We need to get the loc in the source views space, so we look up appropriately in the 938// list of tokens (which uses the views loc range) 939const SourceLoc loc = sourceView -> getRange ().getSourceLocFromOffset (offset ); 940 941// Work out the line number 942SourceFile * sourceFile = sourceView -> getSourceFile (); 943const Index lineIndex = sourceFile -> calcLineIndexFromOffset (int (offset )); 944 945dst .visibilty = lineVisibility [lineIndex ]; 946 947// Okay, lets find the token index with a binary chop 948Index tokenIndex = 949_findTokenIndex (loc ,tokens .m_tokens .getBuffer (),tokens .m_tokens .getCount ()); 950if (tokenIndex >=0 && lineIndex >=0 ) 951 { 952FindInfo findInfo ; 953findInfo .tokenIndex = tokenIndex ; 954findInfo .lineIndex = lineIndex ; 955findInfo .tokenList = & tokens ; 956findInfo .sourceView = sourceView ; 957 958// Okay let's see if we extract some documentation then for this. 959FoundMarkup foundMarkup ; 960SlangResult res = _findMarkup (findInfo ,entry .searchStyle ,foundMarkup ); 961 962if (SLANG_SUCCEEDED (res )) 963 { 964// We need to extract 965StringBuilder buf ; 966SLANG_RETURN_ON_FAIL (_extractMarkup (findInfo ,foundMarkup ,buf )); 967 968// Save the extracted text in the output 969dst .text = buf ; 970 } 971else if (res != SLANG_E_NOT_FOUND ) 972 { 973return res ; 974 } 975 } 976 } 977 } 978 979return SLANG_OK ; 980} 981 982}// namespace Slang